Microsoft's Peer-to-Peer Graphing technology provides a stable, reliable, and robust infrastructure for Windows peer-to-peer applications to communicate. Peers use Peer Name Resolution Protocol (PNRP - a serverless DNS) to register and discover other peers within the graph. Graphs are the foundation for connecting peers, services, and resources within a peer network. A peer can be a user-interactive application, service or resource. Graphing allows data to be passed between peers efficiently and reliably.
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#.
This article introduces the concepts of peers exchanging private data in a peer-to-peer graph. It is a continuation of the last article which shows how peers can open and close private connections using Microsoft's Peer-to-Peer Graphing technology. Once a connection is opened between peers, either peer can send and receive data over the connection. The
PeerOpenConnection class provides two additional methods for sending data and an additional event for receiving data.
To send data, the sample
PeerGraph class provides the following
internal function for sending an array of bytes.
internal void SendData(System.Int64 ConnectionId,
Guid Type, int Length, IntPtr Data)
IntPtr typeptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid)));
Marshal.StructureToPtr(Type, typeptr, false);
uint hr = PeerGraphNative.PeerGraphSendData(hGraph,
ConnectionId, typeptr, (uint)Length, Data);
if (hr != 0) throw new PeerGraphException(hr);
SendData method calls the underlying
PeerGraphSendData API. The ID of the connection, the message type, the buffer length, and the buffer are passed as parameters. This method returns as soon as the data is on the network and does not wait for an acknowledgement.
PeerOpenConnection class implements two cover functions to simplify sending data. The first method allows a string to be sent.
public void SendData(Guid Type, string Message)
IntPtr dataptr = Marshal.StringToHGlobalUni(Message);
int size = (Message.Length+1)*2;
graph.SendData(connectionId, Type, size, dataptr);
The second method provides a more general stream of data to be sent. A
MemoryStream is an ideal way to exchange structured data between peers. This also allows passing a file stream to send the contents of a file.
public void SendData(Guid Type, System.IO.Stream Data)
int size = (int)Data.Length;
byte data = new byte[size];
Data.Read(data, 0, size);
IntPtr dataptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, dataptr, false);
graph.SendData(connectionId, Type, size, dataptr);
In both cases, the gory details of marshalling managed data to unmanaged pointers are handled.
You might have noticed that the
SendData methods take a
Type parameter. This GUID value allows each type of message to be uniquely identified. This is the same as a Windows applications using the
WM_XXX codes to identify messages.
Data Received Event
PeerGraph class handles the
PEER_GRAPH_EVENT_INCOMING_DATA notification in the order of receiving private data messages from another peer.
private void HandleEventIncomingData(IntPtr evptr)
PEER_EVENT_INCOMING_DATA ndata = (PEER_EVENT_INCOMING_DATA)
PeerOpenConnection cn = (PeerOpenConnection)OpenConnections[ndata.ullConnectionId];
if (cn != null)
PeerGraphDataReceiveEventArgs args = new
PeerGraph class maintains a hash table mapping all connection IDs to open connections. A quick lookup allows the incoming notification to fire the
DataRecieved event on the appropriate, open connection. In this case, the
RaiseDataReceivedEvent function handles this.
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 begins 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 left list shows a diagnostic log of all actions and incoming events. Double-click to clear the list.
The right list shows the identity of all peers connected to the graph. Double-click on a peer to initiate sending a message. An input box appears, for you to enter some text. On closing this window, the text is sent to the corresponding peer.
private Guid WHISPER_MESSAGE_TYPE =
new Guid(0x4D5B2F11, 0x6522, 0x433B, 0x84,
0xEF, 0xA2, 0x98, 0xE6, 0x7, 0xBB, 0xBB);
private void OnConnectionOpened(object sender,
if (e.OpenConnection.ConnectionId == connectionId)
e.OpenConnection.DataReceived += new
OnConnectionOpened event of the sample application compares the connection ID to the ID returned after opening a connection. Matching IDs indicate the peer that opened the connection and will be sending the data. Otherwise, its the peer receiving the data and so it binds for the
public void OnDataReceived(object sender,
The sample application's
DataRecieved event handler simply adds the message received to the diagnostic list and closes the connection.
Points of Interest
The sample applications shows a very simple way for peers to exchange private data. However, this is the basis for a more complete Instant Messenger style of application. It would also be possible to use this technique to exchange files between peers.
Links to Resources
I have found the following resources to be very useful in understanding peer graphs:
I hope you have found this article useful. The next article will focus on sharing data with all peers in a graph. Stay tuned for more articles on the following topics:
- Peer Name Resolution - Windows Vista Enhancements
- 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.
Adrian Moore is the Development Manager for the SCADA Vision system developed by ABB Inc in Calgary, Alberta.
He has been interested in compilers, parsers, real-time database systems and peer-to-peer solutions since the early 90's. In his spare time, he is currently working on a SQL parser for querying .NET DataSets (http://www.queryadataset.com).
Adrian is a Microsoft MVP for Windows Networking.