Peer Graph - Peers and Connections






3.19/5 (7 votes)
Nov 17, 2005
5 min read

52046

1040
Peer Graph - Peers and Connections using Microsoft's Peer-to-Peer technology.
Background
This article introduces the concepts of Peers and Connections in Peer-to-Peer Graphing. It is a continuation of the first article which introduces Microsoft's Peer-to-Peer Graphing technology and shows you how to create, open, close and delete a graph. Just to recap, graphing provides a stable, reliable, and robust infrastructure for Windows peer-to-peer applications to communicate. Graphs are the foundation for connecting peers, services and resources within a peer network. Peers use Peer Name Resolution Protocol (PNRP - a server less DNS) to register and discover other peers within the graph.
A graph consists of peers (sometimes referred to as nodes). A peer can be a user-interactive application, service or resource. Graphing allows data to be passed between peers efficiently and reliably.
Introduction
Microsoft's entire Peer-to-Peer technology is exposed through the latest Platform SDK as C/C++ API calls. However, the code in this article shows these APIs being used from .NET managed code using C#. The sample application includes a PeerGraph
class that includes additional events to notify when peers connect and disconnect, connections are opened and closed, and methods to open and close a direct communication connection between two peers. While the PeerGraph
class hides the details of using Microsoft's Peer-to-Peer Graphing APIs (in order to simplify the programming model), the flow of unmanaged calls is described below. The remainder of this article focuses on peer notifications and opening and closing a direct communication connection between peers.
Peer Connected and Disconnected Event
When a new peer connects to or disconnects from the graph, all other peers receive a PEER_GRAPH_EVENT_NODE_CHANGED
notification. The PeerGraph
class handles this notification and, in turn, fires one of three events as shown below.
private void HandleEventPeerChanged(IntPtr evptr)
{
PEER_EVENT_NODE_CHANGE_DATA ndata =
(PEER_EVENT_NODE_CHANGE_DATA)Marshal.PtrToStructure(evptr,
typeof(PEER_EVENT_NODE_CHANGE_DATA));
// if local node address has changed, re-register with PNRP
if (ndata.changeType == PeerNodeAction.Updated && ndata.ullNodeId == 0)
RegisterAddress();
else
{
PeerGraphPeerChangedEventArgs args = new
PeerGraphPeerChangedEventArgs(ndata.ullNodeId, ndata.pwzPeerName);
switch (ndata.changeType)
{
case PeerNodeAction.Connected:
if (PeerConnected != null) PeerConnected(this, args);
break;
case PeerNodeAction.Disconnected:
if (PeerDisconnected != null) PeerDisconnected(this, args);
break;
case PeerNodeAction.Updated:
if (PeerUpdated != null) PeerUpdated(this, args);
break;
}
}
}
Depending on the type of change, either a PeerConnected
, PeerDisconnected
or PeerUpdated
event is fired. The ID and name of the peer is included in the event. The ID can be passed to the GetPeerInfo
method of PeerGraph
to get more details about the peer as shown next:
public PeerNode GetPeerInfo(System.Int64 peerId)
{
IntPtr pNodeInfo;
uint hr = PeerGraphNative.PeerGraphGetNodeInfo(hGraph, peerId, out pNodeInfo);
if (hr != 0) throw new PeerGraphException(hr);
PeerNode node = new PeerNode((PEER_NODE_INFO)Marshal.PtrToStructure(pNodeInfo,
typeof(PEER_NODE_INFO)));
PeerGraphNative.PeerGraphFreeData(pNodeInfo);
return node;
}
The underlying PeerGraphGetNodeInfo
API method is called which returns a PEER_NODE_INFO
data structure. This data structure is marshaled and converted to a managed PeerNode
class. The PeerNode
class has properties for the peer's name, address and attributes. An instance of a PeerNode
is used when opening a direct connection with that peer.
Opening a Direct Connection
To open a direct connection with another peer, the sample application uses the following code:
PeerNode peer = graph.GetPeerInfo(Convert.ToInt64(listBox2.SelectedItem));
connection = graph.OpenDirectConnect(peer); // step 1 - open direct connection
The selected Peer's ID is passed to GetPeerInfo
to get information about its name and address. This information is passed to the graph's OpenDirectConnection
method.
public System.Int64 OpenDirectConnect(PeerNode node)
{
System.Int64 connectId;
uint hr = PeerGraphNative.PeerGraphOpenDirectConnection(hGraph,
node.PeerName, ref node.addresses[0], out connectId);
if (hr != 0) throw new PeerGraphException(hr);
return connectId;
}
The OpenDirectMethod
calls the underlying PeerGraphOpenDirectConnection
API which returns a connection ID. This method is asynchronous and will result in a PEER_GRAPH_EVENT_DIRECT_CONNECTION
notification to inform the application of the success or failure of the request. The PeerGraph
class handles this notification and fires one of three events as shown below:
private void HandleEventConnectionChanged(IntPtr evptr, PeerConnectionType type)
{
PEER_EVENT_CONNECTION_CHANGE_DATA ndata = (PEER_EVENT_CONNECTION_CHANGE_DATA)
Marshal.PtrToStructure(evptr, typeof(PEER_EVENT_CONNECTION_CHANGE_DATA));
//...
switch (ndata.status)
{
case PeerConnectionStatus.Connected:
if (ConnectionOpened != null)
{
foreach (PeerConnection cn in Connections)
{
if (cn.ConnectionId == ndata.ullConnectionId)
{
PeerGraphConnectionOpenedEventArgs args = new
PeerGraphConnectionOpenedEventArgs(type,
this.OnNewDirectConnection(this, cn));
ConnectionOpened(this, args);
break;
}
}
}
break;
case PeerConnectionStatus.Disconnected:
if (ConnectionClosed != null)
{
PeerGraphConnectionClosedEventArgs args = new
PeerGraphConnectionClosedEventArgs(type,
ndata.ullConnectionId, ndata.ullNodeId);
ConnectionClosed(this, args);
}
break;
case PeerConnectionStatus.Failed:
if (ConnectionFailed != null)
{
PeerGraphConnectionFailedEventArgs args = new
PeerGraphConnectionFailedEventArgs(type, ndata.ullConnectionId);
ConnectionFailed(this, args);
}
break;
}
}
Depending on the result of opening the direction connection, either a ConnectionOpened
, ConnectionClosed
or ConnectionFailed
event is fired. For convenience, before firing the ConnectionOpened
event, the PeerGraph
handler retrieves the details of the connection and passes this with the event arguments. Note that these details are not available after a disconnect or failure. On a disconnect, only the connection ID and peer ID are known. On failure, only the connection ID is known.
Connection Opened Event
The sample application overrides the ConnectionOpened
event as shown below:
private void OnConnectionOpened(object sender, PeerGraphConnectionOpenedEventArgs e)
{
System.Int64 ConnectionId = e.DirectConnection.Connection.ConnectionId;
string PeerName = e.DirectConnection.Connection.PeerName;
if (e.Type == PeerConnectionType.Direct)
// step 2 - local and remote peer recieve this event
{
LogMessage(@"Direct", string.Format("{0} connected ({1})",
PeerName, ConnectionId));
Connections.Add(ConnectionId, e.DirectConnection);
listBox3.Items.Add(ConnectionId.ToString());
}
else
LogMessage(@"Neighbor",
string.Format("{0} connected ({1})", PeerName, ConnectionId));
}
This event is received by both peers that form a direct connection. The sample application logs a message and caches the direct connection object.
Closing a Direct Connection
To close a direct connection with another peer, the sample application uses the following code:
PeerDirectConnection connection =
(PeerDirectConnection)Connections[Convert.ToInt64(listBox3.SelectedItem)];
if (connection != null) connection.Close();
// NOTE: cleanup handled in OnConnectionClosed
The selected connection ID is used to look for a matching cached direct connection object. If one is found, its Close
method is called to close the corresponding direct connection.
Connection Closed Event
The sample application also overrides the ConnectionClosed
event. Again, this event is received by both peers that formed the direction connection. When it receives this event, it simply removes the corresponding direct connection object from the cache.
private void OnConnectionClosed(object sender,
PeerGraphConnectionClosedEventArgs e)
{
if (e.Type == PeerConnectionType.Direct)
{
System.Int64 ConnectionId = e.ConnectionId;
LogMessage(@"Direct",
string.Format("{0} disconnected", ConnectionId));
Connections.Remove(ConnectionId);
listBox3.Items.Remove(ConnectionId.ToString());
}
else
LogMessage(@"Neighbor",
string.Format("{0} disconnected", e.ConnectionId));
}
Using the Sample Application
The sample application lets you first create a graph (unsecured peer name 0.TestGraph
) with an initial identity. The first instance should be opened with this identity. It will pause a few seconds looking for other instances, then begin to listen. Each subsequent instance of the application should open the graph with a different identity. These instances will connect to the nearest peer and synchronize. Each instance of the application is a peer.
The Peers list shows the ID of all peers connected to the graph. Click on a peer to see its properties. Double-click on a peer to open a direction connection. Note that multiple connections can be created between the same pair of peers.
The Connections list shows the ID of all connections opened to a given peer. Click on a connection to see its properties. Double-click on a connection to disconnect. Note that the Connection ID can be different for each peer with an open connection. A ConnectionId
is simply a local handle resource.
The bottom list in the sample application shows a diagnostic log of all actions and incoming events. Double-click to clear the list.
Point of Interest
The PeerGraph
class has some extensibility features which are not demonstrated by the sample application. It's possible to derive from the PeerGraph
class and override the method OnNewDirectConnection
. By default, this method creates a new PeerDirectConnection
class. You can derive your own MyDirectConnection
class and override the OnNewDirectConnection
to create it.
Links to Resources
I have found the following resources to be very useful in understanding peer graphs:
Conclusion
I hope you have found this article useful. The next article will focus on exchanging private data over a direct connection and provide enough infrastructure to build a simple chat application. Stay tuned for more articles on the following topics:
- Peer Name Resolution - Windows Vista Enhancements
- Peer Graph - Exchanging Private Data
- Peer Graph - Records
- Peer Graph - Attributes
- Peer Graph - Searching
- Peer Groups and Identity
- Peer Collaboration - People Near Me
- Peer Collaboration - EndPoints
- Peer Collaboration - Capabilities
- Peer Collaboration - Presence
- Peer Collaboration - Invitations
If you have suggestions for other topics, please leave a comment.
History
Initial revision.