Background
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#.
Introduction
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.
Sending 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);
}
The 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.
The sample 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
The 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)
Marshal.PtrToStructure(evptr,
typeof(PEER_EVENT_INCOMING_DATA));
PeerOpenConnection cn = (PeerOpenConnection)OpenConnections[ndata.ullConnectionId];
if (cn != null)
{
PeerGraphDataReceiveEventArgs args = new
PeerGraphDataReceiveEventArgs(cn, ndata.type,
ndata.data.pbData, ndata.data.cbData);
cn.RaiseDataReceivedEvent(args);
}
}
Internally, the 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,
PeerGraphConnectionOpenedEventArgs e)
{
if (e.OpenConnection.ConnectionId == connectionId)
{
e.OpenConnection.SendData(WHISPER_MESSAGE_TYPE, Message);
}
else
e.OpenConnection.DataReceived += new
PeerOpenConnection.DataReceivedHandler(OnDataReceived);
}
The 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 DataReceived
event.
public void OnDataReceived(object sender,
PeerGraphDataReceiveEventArgs e)
{
LogMessage(e.OpenConnection.Connection.PeerName, e.DataAsString);
e.OpenConnection.Close();
}
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:
Conclusion
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.
History
Initial revision.