Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / serverless

Peer Graph - Exchanging Private Data

4.23/5 (3 votes)
12 Dec 20054 min read 2   390  
Exchanging private data in a peer graph using Microsoft's Peer-to-Peer technology.

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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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)
  {
    // send the data
    e.OpenConnection.SendData(WHISPER_MESSAGE_TYPE, Message);
  }
  else // receiver opened, so bind for event
    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.

C#
public void OnDataReceived(object sender, 
               PeerGraphDataReceiveEventArgs e)
{
  // read message then close
  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:

  1. Peer Name Resolution - Windows Vista Enhancements
  1. Peer Graph - Records
  2. Peer Graph - Attributes
  3. Peer Graph - Searching
  1. Peer Groups and Identity
  1. Peer Collaboration - People Near Me
  2. Peer Collaboration - EndPoints
  3. Peer Collaboration - Capabilities
  4. Peer Collaboration - Presence
  5. Peer Collaboration - Invitations

If you have suggestions for other topics, please leave a comment.

History

Initial revision.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here