Chapter 46

Peer-to-Peer Networking

WHAT’S IN THIS CHAPTER?

  • Learning about P2P
  • Using the Microsoft Windows 8 P2P Networking platform
  • Registering and Resolving Peer Names
  • Sending and receiving messages across peers
  • Building P2P applications with the .NET Framework

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the wrox.com code downloads for this chapter at http://www.wrox.com/remtitle.cgi?isbn=1118314425 on the Download Code tab. The code for this chapter contains this major example:

  • P2PSample

PEER-TO-PEER NETWORKING OVERVIEW

Peer-to-peer networking, often referred to as P2P, is perhaps one of the most useful and yet misunderstood technologies to emerge in recent years. When people think of P2P, they usually think of one thing: sharing music video, software, and other files, often illegally. This is because file-sharing applications such as BitTorrent have risen in popularity at a staggering rate, and these applications use P2P technology to work.

Although P2P is used in file-sharing applications, that doesn’t mean it doesn’t have other applications. Indeed, as you see in this chapter, you can use P2P for a vast array of applications, and it is becoming more important in the interconnected world in which you live. You learn about this in the first part of this chapter, when you look at an overview of P2P technologies.

Microsoft has not been oblivious to the emergence of P2P and has been developing its own tools and technologies to use it. You can use the Microsoft Windows Peer-to-Peer Networking platform as a communication framework for P2P applications. This platform includes the important component Peer Name Resolution Protocol (PNRP). The .NET Framework contains the namespace, System.Net.PeerToPeer, and several types and features that you can use to build P2P applications with minimal effort.

Peer-to-peer networking is an alternative approach to network communication. To understand how P2P differs from the “standard” approach to network communication, you need to take a step backward to look at client-server communications. Client-server communications are ubiquitous in networked applications today.

Client-Server Architecture

Traditionally, you interact with applications over a network (including the Internet) using a client-server architecture. Websites are a great example of this. When you look at a website, you send a request over the Internet to a web server, which then returns the information that you require. If you want to download a file, you do so directly from the web server.

Similarly, desktop applications that include local or wide area network connectivity typically connect to a single server, for example, a database server or a server that hosts other services.

This simple form of client-server architecture is shown in Figure 46-1.

There is nothing inherently wrong with the client-server architecture, and indeed in many cases it is exactly what you want. However, there is a scalability problem. Figure 46-2 shows how the client-server architecture scales with additional clients.

With every client added an increased load is placed on the server, which must communicate with each client. To return to the website example, this increased communication load is how websites collapse. When there is too much traffic, the server simply becomes unresponsive.

There are, of course, scaling options that you can implement to mitigate this situation. You can scale up by increasing the power and resources available to the server, or you can scale out by adding additional servers. Scaling up is limited by the technology available and the cost of better hardware. Scaling out is potentially more flexible but requires an additional infrastructure layer to ensure that clients either communicate with individual servers or maintain session state independent of the server with which they communicate. Plenty of solutions are available for this, such as web or server farm products.

P2P Architecture

The peer-to-peer approach is completely different from either the scaling up or scaling out approach. With P2P, instead of focusing on and attempting to streamline the communication between the server and its clients, you instead look at ways in which clients can communicate with each other.

Say, for example, that the website that clients communicate with is www.wrox.com. In your imaginary scenario, Wrox has announced that a new version of this book is to be released on the wrox.com website and will be free to download to anyone who wants it; however, it will be removed after one day. Before the book becomes available on the website you might imagine that many people will look at the website and refresh their browsers, waiting for the file to appear. When the file is available, everyone will try to download it at the same time, and more than likely the wrox.com web server will collapse under the strain.

You could use P2P technology to prevent this web server collapse. Instead of sending the file directly from the server to all the clients, you send the file to just a few clients. A few of the remaining clients then download the file from the clients that already have it; a few more clients download it from those second-level clients; and so on. This process is made even faster by splitting the file into chunks and dividing these chunks among clients, some of whom download it directly from the server, and some whom download chunks from other clients. This is how file-sharing technologies such as BitTorrent work, as shown in Figure 46-3.

P2P Architectural Challenges

You still need to solve problems in the file-sharing architecture discussed here. For a start, how do clients detect that other clients exist, and how do they locate chunks of the file that other clients might have? Also, how can you ensure optimal communication between clients that may be separated by entire continents?

Every client participating in a P2P network application must perform the following operations to overcome these problems:

  • It must discover other clients.
  • It must connect to other clients.
  • It must communicate with other clients.

The discovery problem has two obvious solutions. You can either keep a list of the clients on the server so that clients can obtain this list and contact other clients (known as peers), or you can use an infrastructure (for example PNRP, covered in the section “Peer Name Resolution Protocol”) that enables clients to find each other directly. Most file-sharing systems use the “list on a server” solution by using servers known as trackers. Also, in file-sharing systems any client may act as a server, as shown in Figure 46-3, by declaring that it has a file available and registering it with a tracker. In fact, a pure P2P network doesn’t need servers, just peers.

The connection problem is a more subtle one and concerns the overall structure of the networks used by a P2P application. If you have one group of clients, all of which can communicate with one another, the topology of the connections between these clients can become extremely complex. You can often improve performance by having more than one group of clients, each of which consists of connections between clients in that group, but not to clients in other groups. If you can make these groups locale-based, you can get an additional performance boost because clients can communicate with each other with fewer hops between networked computers.

Communication is perhaps a problem of lesser importance because communication protocols such as TCP/IP are well established and can be reused here. There is, however, scope for improvement in both high-level technologies (for example, you can use WCF services and therefore all the functionality that WCF offers) and low-level protocols (such as multicast protocols to send data to multiple endpoints simultaneously).

Discovery, connection, and communication are central to any P2P implementation. The implementation you look at in this chapter is to use the System.Net.PeerToPeer types with PNM for discovery and PNRP for connection. As you see in subsequent sections, these technologies cover all three operations.

P2P Terminology

In the previous sections you were introduced to the concept of a peer, which is how clients are referred to in a P2P network. The word client makes no sense in a P2P network because there is not necessarily a server to be a client of.

Groups of peers connected to each other are known by the interchangeable terms meshes, clouds, or graphs. A given group can be said to be well connected if at least one of the following statements applies:

  • There is a connection path between every pair of peers so that every peer can connect to any other peer as required.
  • There are a relatively small number of connections to traverse between any pair of peers.
  • Removing a peer does not prevent other peers from connecting to each other.

This does not mean that every peer must connect to every other peer directly. If you analyze a network mathematically, you can find that peers need to connect only to a relatively small number of other peers for these conditions to be met.

Another P2P concept to be aware of is flooding. Flooding is the way in which a single piece of data may be propagated through a network to all peers, or querying other nodes in a network to locate a specific piece of data. In unstructured P2P networks this is a fairly random process to contact nearest neighbor peers, which in turn contact their nearest neighbors, and so on until every peer in the network is contacted. You can also create structured P2P networks so that there are well-defined pathways for queries and data flow among peers.

P2P Solutions

When you have an infrastructure for P2P, you can start to develop not only improved versions of client-server applications, but also entirely new applications. P2P is particularly suited to the following classes of applications:

  • Content distribution applications, including the file-sharing applications discussed earlier
  • Collaboration applications, such as desktop sharing and shared whiteboard applications
  • Multi-user communication applications that enable users to communicate and exchange data directly rather than through a server
  • Distributed processing applications, as an alternative to supercomputing applications that process enormous amounts of data
  • Web 2.0 applications that combine some or all the preceding points in dynamic, next-generation web applications

PEER NAME RESOLUTION PROTOCOL (PNRP)

The Microsoft Windows Peer-to-Peer Networking platform is Microsoft’s implementation of P2P technology. It is part of Windows since Windows XP SP2. You can use the Peer Name Resolution Protocol (PNRP) to publish and resolve peer addresses. In this section you learn about this protocol.

You can use any protocol at your disposal to implement a P2P application, but if you work in a Microsoft Windows 8 environment (and if you’re reading this book you probably are) it makes sense to at least consider PNRP. In itself, PNRP doesn’t give you everything you need to create a P2P application. Rather, it is one of the underlying technologies that you use to resolve peer addresses. PNRP enables a client to register an endpoint (known as a peer name) that is automatically circulated among peers in a cloud. This peer name is encapsulated in a PNRP ID. A peer that discovers the PNRP ID can use PNRP to resolve it to the actual peer name and can then communicate directly with the associated client.

For example, you might define a peer name that represents a WCF service endpoint. You could use PNRP to register this peer name in a cloud as a PNRP ID. A peer running a suitable client application that uses a discovery mechanism that can identify peer names for the service you are exposing might then discover this PNRP ID. When discovered, the peer would use PNRP to locate the endpoint of the WCF service and then use that service.


NOTE PNRP makes no assumptions about what a peer name actually represents. It is up to peers to decide how to use them when discovered. The information a peer receives from PNRP when resolving a PNRP ID includes the IPv6 (and usually also the IPv4) address of the publisher of the ID, along with a port number and optionally a small amount of additional data. Unless the peer knows what the peer name means, it is unlikely to do anything useful with this information.

PNRP IDs

PNRP IDs are 256-bit identifiers. The low-order 128 bits can uniquely identify a particular peer, and the high-order 128 bits identify a peer name. The high-order 128 bits are a hashed combination of a hashed public key from the publishing peer and a string of up to 149 characters that identifies the peer name. The hashed public key (known as the authority) combined with this string (the classifier) are together referred to as the P2P ID. You can also use a value of 0 instead of a hashed public key, in which case the peer name is said to be unsecured (as opposed to secured peer names, which use a public key).

Figure 46-4 uses the structure of a PNRP ID.

The PNRP service on a peer is responsible to maintain a list of PNRP IDs, including the ones that it publishes and a cached list of those it has obtained by PNRP service instances elsewhere in the cloud. When a peer attempts to resolve a PNRP ID, the PNRP service either uses a cached copy of the endpoint to resolve the peer that published the PNRP or it asks its neighbors if they can resolve it. Eventually a connection to the publishing peer is made, and the PNRP service can resolve the PNRP ID.

All this happens without you intervening in any way. All you must do is ensure that peers know what to do with peer names after they have resolved them using their local PNRP service.

Peers can use PNRP to locate PNRP IDs that match a particular P2P ID. You can use this to implement a basic form of discovery for unsecured peer names. This is because if several peers expose an unsecured peer name that uses the same classifier, the P2P ID will be the same. Of course, because any peer can use an unsecured peer name, you have no guarantee that the endpoint you connect to will be the sort of endpoint you expect, so this is only a viable solution for discovery over a local network.

PNRP Clouds

In the preceding discussion you learned how PNRP registers and resolves peer names in clouds. A cloud is maintained by a seed server, which can be any server running the PNRP service that maintains a record of at least one peer. Two types of clouds are available to the PNRP service:

  • Link local: These clouds consist of the computers attached to a local network. A PC may connect to more than one link local cloud if it has multiple network adapters.
  • Global: This cloud consists of computers connected to the Internet by default; although you can define a private global cloud. The difference is that Microsoft maintains the seed server for the global Internet cloud, whereas if you define a private global cloud you must use your own seed server. If you use your own seed server, you must ensure that all peers connect to it by configuring policy settings.

NOTE In previous versions of PNRP, a third type of cloud existed: site local. This is no longer used and is not covered in this chapter.

You can discover what clouds you are connected to with the following command:

netsh p2p pnrp cloud show list

A typical result is shown here:

Scope  Id     Addr   State             Name
-----  -----  -----  ----------------  -----
    1      0      1  Virtual           Global_
    3     13      1  Virtual           LinkLocal_ff00::%13/8
    3     19      1  Virtual           LinkLocal_ff00::%19/8

The output shows that three clouds are available: one is a global and two are link local clouds. You can tell this from both the name and the Scope value, which is 3 for link local clouds and 1 for global clouds. To connect to a global cloud, you must have an IPv6 address.

Clouds may be in one of the following states:

  • Active: If the state of a cloud is active, you can use it to publish and resolve peer names.
  • Alone: If the peer you query the cloud from is not connected to any other peers, it has a state of alone.
  • No Net: If the peer is not connected to a network, the cloud state may change from active to no net.
  • Synchronizing: Clouds are in the synchronizing state when the peer connects to them. This state changes to another state extremely quickly because this connection does not take long, so you will probably never see a cloud in this state.
  • Virtual: The PNRP service connects to clouds only as required by peer name registration and resolution. If a cloud connection has been inactive for more than 15 minutes, it may enter the virtual state.

NOTE If you experience network connectivity problems, you should check your firewall to see if it prevents local network traffic over the UDP ports 3540 or 1900. UDP port 3540 is used by PNRP, and UDP port 1900 is used by the Simple Service Discovery Protocol (SSDP), which in turn is used by the PNRP service (and UPnP devices).

PNRP Since Windows 7

Since Windows 7, PNRP makes use of a component called the Distributed Routing Table (DRT). This component is responsible to determine the structure of the keys used by PNRP; the default implementation of which is the PNRP ID previously described. By using the DRT API you can define an alternative key scheme, but the keys must be 256-bit integer values (just like PNRP IDs). This means that you can use any scheme you want, but you are then responsible for the generation and security of the keys. By using this component you can create new cloud topologies beyond the scope of PNRP, and indeed, beyond the scope of this chapter because this is an advanced technique.

Windows 7 also introduced a new way to connect to other users for the Remote Assistance application: Easy Connect. This connection option uses PNRP to locate users to connect to. After a session is created, through Easy Connect or by other means (for example an e-mail invitation), users can share their Desktops and assist each other through the Remote Assistance interface.

BUILDING P2P APPLICATIONS

Now that you have learned what P2P networking is and what technologies are available to .NET developers to implement P2P applications, it’s time to look at how you can build them. From the preceding discussion you know that you will use PNRP to publish, distribute, and resolve peer names, so the first thing you look at here is how to achieve that using .NET. Next you look at how to use PNM as a framework for a P2P application. This can be advantageous because if you use PNM you do not need to implement your own discovery mechanisms.

To examine these subjects you need to learn about the classes in the System.Net.PeerToPeer namespace. For these classes you must have a reference to the System.Net assembly.

The classes in the System.Net.PeerToPeer namespace encapsulate the API for PNRP and enable you to interact with the PNRP service. You can use these classes for two main tasks:

  • Registering peer names
  • Resolving peer names

In the following sections, all the types referred to come from the System.Net.PeerToPeer namespace unless otherwise specified.

Registering Peer Names

To register a peer name follow these steps:

1. Create a secured or unsecured peer name with a specified classifier.
2. Configure a registration for the peer name, providing as much of the following optional information as you choose:
  • A TCP port number.
  • The cloud or clouds with which to register the peer name. (If unspecified, PNRP registers the peer name in all available clouds.)
  • A comment of up to 39 characters.
  • Up to 4,096 bytes of additional data.
  • Whether to generate endpoints for the peer name automatically. (The default behavior, where endpoints will be generated from the IP address or addresses of the peer and, if specified, the port number.)
  • A collection of endpoints.
3. Use the peer name registration to register the peer name with the local PNRP service.

After step 3, the peer name is available to all peers in the selected cloud (or clouds). Peer registration continues until it explicitly stops, or until the process that registers the peer name is terminated.

To create a peer name, you use the PeerName class. You create an instance of this class from a string representation of a P2P ID in the form authority.classifier or from a classifier string and a PeerNameType. You can use PeerNameType.Secured or PeerNameType.Unsecured, for example:

var pn = new PeerName("Peer classifier", PeerNameType.Secured);

Because an unsecured peer name uses an authority value of 0, the following lines of code are equivalent:

var pn = new PeerName("Peer classifier", PeerNameType.Unsecured);
             
var pn = new PeerName("0.Peer classifier");

After you have a PeerName instance, you can use it along with a port number to initialize a PeerNameRegistration object:

var pnr = new PeerNameRegistration(pn, 8080);

Alternatively, you can set the PeerName and (optionally) the Port properties on a PeerNameRegistration object created using its default parameter. You can also specify a Cloud instance as a third parameter of the PeerNameRegistration constructor, or through the Cloud property. You can obtain a Cloud instance from the cloud name or by using one of the following static members of Cloud:

  • Cloud.Global: This static property obtains a reference to the global cloud. This may be a private global cloud depending on peer policy configuration.
  • Cloud.AllLinkLocal: This static field gets a cloud that contains all the link local clouds available to the peer.
  • Cloud.Available: This static field gets a cloud that contains all the clouds available to the peer, which includes link local clouds and (if available) the global cloud.

When created, you can set the Comment and Data properties if you want. Be aware of the limitations of these properties, though. You receive a PeerToPeerException if you try to set Comment to a string greater than 39 Unicode characters or an ArgumentOutOfRangeException if you try to set Data to a byte[] greater than 4,096 bytes. You can also add endpoints by using the EndPointCollection property. This property is a System.Net.IPEndPointCollection collection of System.Net.IPEndPoint objects. If you use the EndPointCollection property you might also want to set the UseAutoEndPointSelection property to false to prevent automatic generation of endpoints.

When you are ready to register the peer name, you can call the PeerNameRegistration.Start method. To remove a peer name registration from the PNRP service, use the PeerNameRegistration.Stop method.

The following code registers a secured peer name with a comment:

var pn = new PeerName("Peer classifier", PeerNameType.Unsecured);
var pnr = new PeerNameRegistration(pn, 8080);
pnr.Comment = "Get pizza here";
pnr.Start();

Resolving Peer Names

To resolve a peer name you must carry out the following steps:

1. Generate a peer name from a known P2P ID or a P2P ID obtained through a discovery technique.
2. Use a resolver to resolve the peer name and obtain a collection of peer name records. You can limit the resolver to a particular cloud and a maximum number of results to return.
3. For any peer name records that you obtain, obtain peer name, endpoint, comment, and additional data information as required.

This process starts with a PeerName object similar to a peer name registration. The difference here is that you use a peer name registered by one or more remote peers. The simplest way to get a list of active peers in your link local cloud is for each peer to register an unsecured peer name with the same classifier and to use the same peer name in the resolving phase. However, this is not a recommended strategy for global clouds because unsecured peer names are easily spoofed.

To resolve peer names use the PeerNameResolver class. When you have an instance of this class, you can choose to resolve peer names synchronously by using the Resolve method or asynchronously using the ResolveAsync method.

You can call the Resolve method with a single PeerName parameter, but you can also pass an optional Cloud instance to resolve in, an int maximum number of peers to return, or both. This method returns a PeerNameRecordCollection instance, which is a collection of PeerNameRecord objects. For example, the following code resolves an unsecured peer name in all link local clouds and returns a maximum of five results:

var pn = new PeerName("0.Peer classifier");
var pnres = new PeerNameResolver();
PeerNameRecordCollection pnrc = pnres.Resolve(pn, Cloud.AllLinkLocal, 5);

The ResolveAsync method uses a standard asynchronous method call pattern. You pass a unique userState object to the method and listen for ResolveProgressChanged events for peers being found and the ResolveCompleted event when the method terminates. You can cancel a pending asynchronous request with the ResolveAsyncCancel method.

Event handlers for the ResolveProgressChanged event use the ResolveProgressChangedEventArgs event arguments parameter, which derives from the standard System.ComponentModel.ProgressChangedEventArgs class. You can use the PeerNameRecord property of the event argument object you receive in the event handler to get a reference to the peer name record that was found.

Similarly, the ResolveCompleted event requires an event handler that uses a parameter of type ResolveCompletedEventArgs, which derives from AsyncCompletedEventArgs. This type includes a PeerNameRecordCollection parameter you can use to obtain a complete list of the peer name records that were found.

The following code shows an implementation of event handlers for these events:

    private pnres_ResolveProgressChanged(object sender,
        ResolveProgressChangedEventArgs e)
    {
      // Use e.ProgressPercentage (inherited from base event args)
      // Process PeerNameRecord from e.PeerNameRecord
    }
             
    private pnres_ResolveCompleted(object sender,
        ResolveCompletedEventArgs e)
    {
      // Test for e.IsCancelled and e.Error (inherited from base event args)
      // Process PeerNameRecordCollection from e.PeerNameRecordCollection
    }

After you have one or more PeerNameRecord objects you can proceed to process them. This PeerNameRecord class exposes Comment and Data properties to examine the comment and data set in the peer name registration (if any), a PeerName property to get the PeerName object for the peer name record, and, most important, an EndPointCollection property. As with PeerNameRegistration, this property is a System.Net.IPEndPointCollection collection of System.Net.IPEndPoint objects. You can use these objects to connect to endpoints exposed by the peer in any way you want.

Code Access Security in System.Net.PeerToPeer

The System.Net.PeerToPeer namespace also includes the following two classes that you can use with Code Access Security (CAS). See Chapter 22, “Security,” for more details.

  • PnrpPermission, which inherits from CodeAccessPermission
  • PnrpPermissionAttribute, which inherits from CodeAccessSecurityAttribute

You can use these classes to provide permissions functionality for PNRP access in the usual CAS way.

Sample Application

The downloadable code for this chapter includes a sample P2P application (P2PSample) that uses the concepts and namespace introduced in this section. It is a WPF application that uses a WCF service for a peer endpoint.

The application is configured with an application configuration file, in which you can specify the name of the peer and a port to listen on as follows (code file App.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="username" value="Christian" />
    <add key="port" value="8731" />
  </appSettings>
</configuration>

After you build the application, you can test it either by copying it to other computers in your local network and running all instances, or by running multiple instances on one computer. If you choose the latter option, you must remember to change the port used for each instance by changing individual config files. (Copy the contents of the Debug directory on your local computer and edit each config file in turn.) The results are clearer in both ways to test this application if you also change the username for each instance.

When the peer applications run, you can use the Refresh button to obtain a list of peers asynchronously. When you locate a peer, you can send a default message by clicking the Message button for the peer.

Figure 46-5 shows this application in action with three instances running on one machine. In the figure, one peer has just messaged another, which results in a dialog box.

Now get into the code. With the field members of the class MainWindow (code file MainWindow.xaml.cs) an observable collection is defined that contains all the peers. In the constructor of the class, just one PeerEntry is added to the collection that gives information to the user to click the Refresh button to get all the peers.

  public partial class MainWindow : Window
  {
    private P2PService localService;
    private ServiceHost host;
    private PeerName peerName;
    private PeerNameRegistration peerNameRegistration;
    private ObservableCollection<PeerEntry> peerList = 
      new ObservableCollection<PeerEntry>();
    private object peersLock = new object();
 
    public MainWindow()
    {
      InitializeComponent();
      this.DataContext = peerList;
      peerList.Add(
        new PeerEntry
        {
          DisplayString = "Refresh to look for peers.",
          ButtonsEnabled = false
        });
      BindingOperations.EnableCollectionSynchronization(peerList, peersLock);
    }

Most of the work in this application takes place in the Window_Loaded event handler for the MainWindow window. This method starts by loading configuration information and setting the window title with the username:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
      // Get configuration from app.config
      string port = ConfigurationManager.AppSettings["port"];
      string username = ConfigurationManager.AppSettings["username"];
      string machineName = Environment.MachineName;
      string serviceUrl = null;
             
      // Set window title
      this.Title = string.Format("P2P example — {0}", username);

Next, the peer host address is used along with the configured port to determine the endpoint on which to host the WCF service. The service uses NetTcpBinding binding, so the URL of the endpoint uses the net.tcp protocol:

      // Get service url using IPv4 address and port from config file
      serviceUrl = Dns.GetHostAddresses(Dns.GetHostName())
        .Where(address => address.AddressFamily == AddressFamily.InterNetwork)
        .Select(address => 
          string.Format("net.tcp://{0}:{1}/P2PService", address, port))
        .FirstOrDefault();

The endpoint URL is validated, and then the WCF service is registered and started:

      // Check for null address
      if (serviceUrl == null)
      {
        // Display error and shutdown
        MessageBox.Show(this, "Unable to determine WCF endpoint.",
          "Networking Error", MessageBoxButton.OK, MessageBoxImage.Stop);
        Application.Current.Shutdown();
      }
             
      // Register and start WCF service.
      localService = new P2PService(this, username);
      host = new ServiceHost(localService, new Uri(serviceUrl));
      var binding = new NetTcpBinding();
      binding.Security.Mode = SecurityMode.None;
      host.AddServiceEndpoint(typeof(IP2PService), binding, serviceUrl);
      try
      {
        host.Open();
      }
      catch (AddressAlreadyInUseException)
      {
        // Display error and shutdown
        MessageBox.Show(this, "Cannot start listening, port in use.",
          "WCF Error", MessageBoxButton.OK, MessageBoxImage.Stop);
        Application.Current.Shutdown();
      }

A single instance of the service class enables easy communication between the host app and the service (for sending and receiving messages). Also, security is disabled in the binding configuration for simplicity.

Next, the System.Net.PeerToPeer namespace classes register a peer name:

      // Create peer name
      peerName = new PeerName("P2P Sample", PeerNameType.Unsecured);
             
      // Prepare peer name registration in link local clouds
      peerNameRegistration = new PeerNameRegistration(peerName, int.Parse(port));
      peerNameRegistration.Cloud = Cloud.AllLinkLocal;
             
      // Start registration
      peerNameRegistration.Start();
    }

When the Refresh button is clicked, the RefreshButton_Click event handler uses PeerNameResolver.ResolveAsync to get peers asynchronously:

    private async void RefreshButton_Click(object sender, RoutedEventArgs e)
    {
      // Create resolver and add event handlers
      var resolver = new PeerNameResolver();
      resolver.ResolveProgressChanged +=
        new EventHandler<ResolveProgressChangedEventArgs>(
          resolver_ResolveProgressChanged);
      resolver.ResolveCompleted +=
        new EventHandler<ResolveCompletedEventArgs>(
          resolver_ResolveCompleted);
             
      // Prepare for new peers
      peerList.Clear();
      RefreshButton.IsEnabled = false;
             
      // Resolve unsecured peers asynchronously
      resolver.ResolveAsync(new PeerName("0.P2P Sample"), 1);
    

When peer information is received, the events ResolveProgressChanged and ResolveCompleted events are fired where the peer information is received. If case peers are not active yet, a timeout is defined to cancel the resolve process to fire the ResolveCompleted. The timeout is dealt with the Task.Delay method, and after the timeout ResolveAsyncCancel is invoked with the same user state value, that is passed to the ResolveAsync method. With the user state value, the same resolve task is mapped for cancellation.

      await Task.Delay(5000); 
      resolver.ResolveAsyncCancel(1);
    }

The remainder of the code is responsible to display and communicate with peers, and you can explore it at your leisure.

Exposing WCF endpoints through P2P clouds is a great way to locate services within an enterprise, as well as being an excellent way to communicate between peers, as in this example.

SUMMARY

This chapter demonstrated how to implement peer-to-peer functionality in your applications by using the P2P classes.

You have looked at the types of solutions that P2P makes possible and how these solutions are structured and how to use PNRP with the types in the System.Net.PeerToPeer namespaces. You also learned about the extremely useful technique to expose WCF services as P2P endpoints.

In the next chapter you look at Message Queuing, both with classes from the System.Messaging namespace and with WCF.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset