Peer - to - Peer Networking


Published on

  • Be the first to comment

  • Be the first to like this

Peer - to - Peer Networking

  1. 1. Peer-to-Peer Networking WHAT’S IN THIS CHAPTER? An overview of P2P The Microsoft Windows Peer-to-Peer Networking platform, including PNRP and PNM Building P2P applications with the .NET Framework 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 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, P2P can be used for a vast array of applications, and is becoming more and more important in the interconnected world in which we 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 compo- nents Peer Name Resolution Protocol (PNRP) and People Near Me (PNM). Also, version 3.5 of the .NET Framework introduced a new namespace, System.Net.PeerToPeer, and several new types and features that you can use to build P2P applications yourself with minimal effort. PEER-TO-PEER NETWORKING OVERVIEW Peer-to-peer networking is an alternative approach to network communication. To understand how P2P differs from the “standard” approach to network communication it is helpful to take a step backward and look at client-server communications. Client-server communications are ubiquitous in networked applications today. ➤ ➤ ➤ 45 c45.indd 1c45.indd 1 11/11/09 5:35:13 PM11/11/09 5:35:13 PM
  2. 2. 2 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING Client-Server Architecture Traditionally, you interact with applications over a network (including the Internet) using a client-server architecture. Web sites are a great example of this. When you look at a web site you send a request over the Internet to a web server, which then returns the infor- mation 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 connectiv- ity will 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 illustrated in Figure 45-1. There is nothing inherently wrong with the client-server architecture, and indeed in many cases it will be exactly what you want. However, there is a scalability problem. Figure 45-2 shows how the client-server architecture scales with addi- tional clients. Server Client Request Response FIGURE 45-1 Clients Server FIGURE 45-2 With every client that is added an increased load is placed on the server, which must communicate with each client. To return to the web site example, this increased communication load is how web sites 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 of course 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 are communicating. 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 cli- ents, you instead look at ways in which clients can communicate with each other. Say, for example, that the web site that clients are communicating with is In our imaginary scenario, Wrox has announced that a new version of this book is to be released on the web site 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 web site you might imagine that an awful lot of people will be looking at the web site and refreshing 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 web server will collapse under the strain. c45.indd 2c45.indd 2 11/11/09 5:35:15 PM11/11/09 5:35:15 PM
  3. 3. 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 down- load the file from the clients that already have it, a few more clients download it from those second-level clients, and so on. In fact, 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, and is illustrated in Figure 45-3. Server Clients FIGURE 45-3 P2P Architectural Challenges There are still problems to solve 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 be able to perform the following operations to overcome these problems: It must be able to discover other clients. It must be able to connect to other clients. It must be able to communicate with other clients. The discovery problem has two obvious solutions. You can either keep a list of the clients on the server so clients can obtain this list and contact other clients (known as peers), or you can use an infrastructure (for example PNRP, covered in the next section) that enables clients to find each other directly. Most file-sharing ➤ ➤ ➤ Peer-to-Peer Networking Overview ❘ 3 c45.indd 3c45.indd 3 11/11/09 5:35:16 PM11/11/09 5:35:16 PM
  4. 4. 4 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING 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 45-3, by declaring that it has a file available and registering it with a tracker. In fact, a pure P2P network needs no servers at all, 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 topol- ogy of the connections between these clients can become extremely complex. You can often improve per- formance 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 will 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 implementa- tion 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 of these 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 that are 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 will not prevent other peers from connecting to each other. Note that this does not mean that every peer must be able to connect to every other peer directly. In fact, if you analyze a network mathematically you will 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 of querying other nodes in a network to locate a specific piece of data. In unstructured P2P networks this is a fairly random process of contacting nearest neighbor peers, which in turn contact their nearest neighbors, and so on until every peer in the network is contacted. It is also possible to create structured P2P networks such 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 just improved versions of client-server applications, but 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 ➤ ➤ ➤ ➤ ➤ c45.indd 4c45.indd 4 11/11/09 5:35:16 PM11/11/09 5:35:16 PM
  5. 5. Multi-user communication applications that allow 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 above in dynamic, next-generation web applications MICROSOFT WINDOWS PEER-TO-PEER NETWORKING The Microsoft Windows Peer-to-Peer Networking platform is Microsoft’s implementation of P2P technology. It is part of Windows XP SP2, Windows Vista, and Windows 7, and is also available as an add-on for Windows XP SP1. It includes two technologies that you can use when creating .NET P2P applications: The Peer Name Resolution Protocol (PNRP), which is used to publish and resolve peer addresses The People Near Me server, which is used to locate local peers (currently for Vista and Windows 7 only) In this section you learn about these technologies. Peer Name Resolution Protocol (PNRP) You can of course use any protocol at your disposal to implement a P2P application, but if you are work- ing in a Microsoft Windows environment (and, let’s face it, if you’re reading this book you probably are) it makes sense to at least consider PNRP. There have been two versions of PNRP released to date. PNRP version 1 was included in Windows XP SP2, Windows XP Professional x64 Edition, and Windows XP SP1 with the Advanced Networking Pack for Windows XP. PNRP version 2 was released with Windows Vista, and was made available to Windows XP SP2 users through a separate download (see KB920342 at Windows 7 also uses version 2. Version 1 and version 2 of PNRP are not compatible, and this chapter covers only version 2. 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 encapsu- lated in a PNRP ID. A peer that discovers the PNRP ID is able to 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. Once discovered, the peer would use PNRP to locate the endpoint of the WCF service and then use that service. An important point is that 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 be able to do anything useful with this information. ➤ ➤ ➤ ➤ ➤ Microsoft Windows Peer-to-Peer Networking ❘ 5 c45.indd 5c45.indd 5 11/11/09 5:35:17 PM11/11/09 5:35:17 PM
  6. 6. 6 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING PNRP IDs PNRP IDs are 256-bit identifiers. The low-order 128 bits are used to 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. It is also possible to 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). The structure of a PNRP ID is illustrated in Figure 45-4. PNRP ID 128-bit hashed P2P ID 128-bit service location P2P ID Authority (hashed public key) Classifier (peer name identifier) FIGURE 45-4 The PNRP service on a peer is responsible for maintaining a list of PNRP IDs, including the ones that it publishes as well as 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. Note that all this happens without you having to intervene in any way. All you have to 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 very 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 really 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 be con- nected 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 it is also possible to 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. ➤ ➤ c45.indd 6c45.indd 6 11/11/09 5:35:30 PM11/11/09 5:35:30 PM
  7. 7. In past releases of PNRP there was a third type of cloud, 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 in Figure 45-5. FIGURE 45-5 Figure 45-5 shows that a single cloud is available, and that it is a link local cloud. 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 a global IPv6 address. The computer used to generate Figure 45-5 does not have one, which is why only a local cloud is available. 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 are querying the cloud from is not connected to any other peers, it will have 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 will be in the synchronizing state when the peer is connecting to them. This state will change 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. If you experience network connectivity problems you should check your firewall in case it is preventing 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 (as well as UPnP devices). PNRP in Windows 7 With Windows 7, PNRP makes use of a new component called the Distributed Routing Table (DRT). This component is responsible for determining the structure of the keys used by PNRP, the default implemen- tation of which is the PNRP ID previously described. By using the DRT API it is possible to define an ➤ ➤ ➤ ➤ ➤ Microsoft Windows Peer-to-Peer Networking ❘ 7 c45.indd 7c45.indd 7 11/11/09 5:35:31 PM11/11/09 5:35:31 PM
  8. 8. 8 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING 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 as this is an advanced technique. Windows 7 also introduces a new way of connecting to other users for the Remote Assistance application: Easy Connect. This connection option uses PNRP to locate users to connect to. Once 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. People Near Me PNRP, as you saw in the previous section, is used to locate peers. This is obviously important as an enabling technology when you consider the discovery/connection/ communication process of a P2P application, but in itself is not a complete implementation of any of these stages. The People Near Me service is an implementation of the discovery stage, and enables you to locate peers that are signed in to the Windows People Near Me service in your local area (that is, in a link local cloud that you are connected to). You may have come across this service because it is built into Vista and Windows 7, and is used in the Windows Meeting Space application, which you can use for sharing applications among peers. You can configure this service through the Change People Near Me settings control panel item (you can navigate to this quickly by typing “people” in the Start menu search box). This control panel item displays the dialog box shown in Figure 45-6. After you have signed in, the service is available to any appli- cation that is built to use the PNM service. At the time of writing, PNM is available only on the Windows Vista family of operating systems (and it has been removed from Windows 7). However, it is possible that future service packs or additional downloads may make it available on Windows XP. 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 be using 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 have to implement your own dis- covery mechanisms. To examine these subjects you need to learn about the classes in the following namespaces: System.Net.PeerToPeer System.Net.PeerToPeer.Collaboration To use these classes you must have a reference to the System.Net.dll assembly. ➤ ➤ FIGURE 45-6 c45.indd 8c45.indd 8 11/11/09 5:35:40 PM11/11/09 5:35:40 PM
  9. 9. System.Net.PeerToPeer The classes in the System.Net.PeerToPeer namespace encapsulate the API for PNRP and enable you to interact with the PNRP service. You will 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 you must carry out the following 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 will register 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 will be available to all peers in the selected cloud (or clouds). Peer registration continues until it is explicitly stopped, or until the process that registered 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 repre- sentation 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: PeerName 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: PeerName pn = new PeerName("Peer classifier", PeerNameType.Unsecured); PeerName 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: PeerNameRegistration 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. ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ Building P2P Applications ❘ 9 c45.indd 9c45.indd 9 11/11/09 5:35:40 PM11/11/09 5:35:40 PM
  10. 10. 10 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING 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 that are 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 will 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 you use the PeerNameRegistration.Stop() method. The following code registers a secured peer name with a comment: PeerName pn = new PeerName("Peer classifier", PeerNameType.Unsecured); PeerNameRegistration 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/or 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 that is 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 you 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 5 results: PeerName pn = new PeerName("0.Peer classifier"); PeerNameResolver 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. ➤ ➤ c45.indd 10c45.indd 10 11/11/09 5:35:41 PM11/11/09 5:35:41 PM
  11. 11. 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 ResolveComplete dEventArgs, 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 importantly, 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 21, “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: <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="username" value="Karli" /> <add key="port" value="8731" /> </appSettings> </configuration> code snippet App.config ➤ ➤ Available for download on Available for download on Building P2P Applications ❘ 11 c45.indd 11c45.indd 11 11/11/09 5:35:42 PM11/11/09 5:35:42 PM
  12. 12. 12 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING After you have built 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 will be clearer in both ways of testing this application if you also change the username for each instance. When the peer applications are running, you can use the Refresh button to obtain a list of peers asynchronously. When you have located a peer you can send a default message by clicking the Message button for the peer. Figure 45-7 shows this application in action with three instances running on one machine. In the figure, one peer has just messaged another and this has resulted in a dialog box. FIGURE 45-7 Most of the work in this application takes place in the Window_Loaded() event handler for the Window1 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); code snippet Window1.xaml.cs 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 will use NetTcpBinding binding, so the URL of the endpoint uses the net.tcp protocol: // Get service url using IPv4 address and port from config file foreach (IPAddress address in Dns.GetHostAddresses(Dns.GetHostName())) { if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { serviceUrl = string.Format("net.tcp://{0}:{1}/P2PService", address, port); Available for download on Available for download on c45.indd 12c45.indd 12 11/11/09 5:35:43 PM11/11/09 5:35:43 PM
  13. 13. break; } } 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)); NetTcpBinding 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 singleton instance of the service class is used to enable easy communication between the host app and the service (for sending and receiving messages). Also, note that security is disabled in the binding configuration for simplicity. Next, the System.Net.PeerToPeer namespace classes are used to 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 void RefreshButton_Click(object sender, RoutedEventArgs e) { // Create resolver and add event handlers PeerNameResolver resolver = new PeerNameResolver(); resolver.ResolveProgressChanged += new EventHandler<ResolveProgressChangedEventArgs>( resolver_ResolveProgressChanged); resolver.ResolveCompleted += new EventHandler<ResolveCompletedEventArgs>( resolver_ResolveCompleted); // Prepare for new peers Building P2P Applications ❘ 13 c45.indd 13c45.indd 13 11/11/09 5:35:43 PM11/11/09 5:35:43 PM
  14. 14. 14 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING PeerList.Items.Clear(); RefreshButton.IsEnabled = false; // Resolve unsecured peers asynchronously resolver.ResolveAsync(new PeerName("0.P2P Sample"), 1); } The remainder of the code is responsible for displaying and communicating with peers, and you can explore it at your leisure. Exposing WCF endpoints through P2P clouds is a great way of locating services within an enterprise, as well as being an excellent way to communicate between peers, as in this example. System.Net.PeerToPeer.Collaboration The classes in the System.Net.PeerToPeer.Collaboration namespace provide a framework you can use to create applications that use the People Near Me service and the P2P collaboration API. As mentioned earlier, at the time of writing this is only possible if you are using Windows Vista or Windows 7. You can use the classes in this namespace to interact with peers and applications in a number of ways, including Signing in and signing out Discovering peers Managing contacts and detecting peer presence You can also use the classes in this namespace to invite other users to join an application, and to exchange data between users and applications. However, to do this you need to create your own PNM-capable appli- cations, which is beyond the scope of this chapter. In the following sections, all the types referred to come from the System.Net.PeerToPeer.Collaboration namespace unless otherwise specified. Signing In and Signing Out One of the most important classes in the System.Net.PeerToPeer.Collaboration namespace is the PeerCollaboration class. This is a static class that exposes numerous static methods that you can use for various purposes, as you will see in this and subsequent sections. You can use two of the methods it exposes, SignIn() and SignOut(), to (unsurprisingly) sign in and sign out of the People Near Me service. Both of these methods take a single parameter of type PeerScope, which can be one of the following values: PeerScope.None — If you use this value, SignIn() and SignOut() will have no effect. PeerScope.NearMe — This will sign you in to or out of the link local clouds. PeerScope.Internet — This will sign you in to or out of the global cloud (which may be necessary to connect to a contact who is not currently on your local subnet). PeerScope.All — This will sign you in to or out of all available clouds. If necessary, calling SignIn() will cause the People Near Me configuration dialog to be displayed. When a peer is signed in you can use the PeerCollaboration.LocalPresenceInfo property to a value of type PeerPresenceInfo. This enables standard IM functionality, such as setting your status to away. You can set the PeerPresenceInfo.DescriptiveText property to a Unicode string of up to 255 charac- ters, and the PeerPresenceInfo.PresenceStatus property to a value from the PeerPresenceStatus enumeration. The values that you can use for this enumeration are as follows: PeerPresenceStatus.Away — The peer is away. PeerPresenceStatus.BeRightBack — The peer is away, but will be back soon. ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ ➤ c45.indd 14c45.indd 14 11/11/09 5:35:44 PM11/11/09 5:35:44 PM
  15. 15. PeerPresenceStatus.Busy — The peer is busy. PeerPresenceStatus.Idle — The peer isn’t active. PeerPresenceStatus.Offline — The peer is offline. PeerPresenceStatus.Online — The peer is online and available. PeerPresenceStatus.OnThePhone — The peer is busy with a phone call. PeerPresenceStatus.OutToLunch — The peer is away, but will be back after lunch. Discovering Peers You can obtain a list of peers near you if you are logged in to the link local cloud. You do this by using the PeerCollaboration.GetPeersNearMe() method. This returns a PeerNearMeCollection object contain- ing PeerNearMe objects. You can use the Nickname property of PeerNearMe to obtain the name of a peer, IsOnline, to determine whether the peer is online, and (for lower-level operations) the PeerEndpoints property to determine end- points related to the peer. PeerEndPoints is also necessary if you want to find out the online status of a PeerNearMe. You can pass an endpoint to the GetPresenceInfo() method to obtain a PeerPresenceInfo object, as described in the previous section. Managing Contacts and Detecting Peer Presence Contacts are a way in which you can remember peers. You can add a peer discovered through the People Near Me service and from then onward you can connect to them whenever you are both online. You can connect to a contact through link local or global clouds (assuming you have IPv6 connectivity to the Internet). You can add a contact from a peer that you have discovered by calling the PeerNearMe .AddToContactManager() method. When you call this method you can choose to associate a display name, nickname, and e-mail address with the contact. Typically, though, you will manage contacts by using the ContactManager class. However you manipulate contacts, you will be dealing with PeerContact objects. PeerContact, like PeerNearMe, inherits from the abstract Peer base class. PeerContact has more properties and methods than PeerNearMe. PeerContact includes DisplayName and EmailAddress properties that further describe a PNM peer, for example. Another difference between these two types is that PeerContact has a more explicit relationship with the System.Net.PeerToPeer.PeerName class. You can get a PeerName from a PeerContact through the PeerContact.PeerName property. After you have done this you can proceed to use techniques you looked at earlier to communicate with any endpoints the PeerName exposes. Information about the local peer is also accessible through the ContactManager class, through the static ContactManager.LocalContact property. This gets you a PeerContact property with details of the local peer. You can add PeerNearMe objects to the local list of contacts by using either the ContactManager .CreateContact() or CreateContactAsync() method, or PeerName objects by using the GetContact() method. You can remove contacts represented by a PeerNearMe or PeerName object with the DeleteContact() method. Finally, there are events that you can handle to respond to changes to contacts. For example, you can use the PresenceChanged event to respond to changes of presence for any contacts known by the ContactManager. Sample Application There is a second sample application in the downloadable code for this chapter that illustrates the use of classes in the System.Net.PeerToPeer.Collaboration namespace. This application is similar to the other example, but much simpler. You need two computers that can both sign in to the PNM server to see this application in action, because it enumerates and displays PNM peers from the local subnet. ➤ ➤ ➤ ➤ ➤ ➤ Building P2P Applications ❘ 15 c45.indd 15c45.indd 15 11/11/09 5:35:44 PM11/11/09 5:35:44 PM
  16. 16. 16 ❘ CHAPTER 45 PEER-TO-PEER NETWORKING When you run the application with at least one peer available for discovery the display will be similar to Figure 45-8. The code is structured in the same way as the previous example, so if you’ve read through that code you should be familiar with this code. This time there is not much work to do in the Window_Loaded() event handler except sign in, because there is no WCF service to initialize or peer name registration to achieve: private void Window_Loaded(object sender, RoutedEventArgs e) { // Sign in to PNM PeerCollaboration.SignIn(PeerScope.NearMe); To make things look a little nicer, though, ContactManager.LocalContact.Nickname is used to format the window title: // Get local peer name to display this.Title = string.Format("PNMSample — {0}", ContactManager.LocalContact.Nickname); } In Window_Closing() the local peer is automatically signed out of PNM: private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { // Sign out of PNM PeerCollaboration.SignOut(PeerScope.NearMe); } Most of the work is done in the RefreshButton_Click() event handler. This uses the PeerCollaboration.GetPeersNearMe() method to obtain a list of peers and add those peers to the display using the PeerEntry class defined in the project, or display a failure message if none are found: private void RefreshButton_Click(object sender, RoutedEventArgs e) { // Get local peers PeerNearMeCollection peersNearMe = PeerCollaboration.GetPeersNearMe(); // Prepare for new peers PeerList.Items.Clear(); // Examine peers foreach (PeerNearMe peerNearMe in peersNearMe) { PeerList.Items.Add( new PeerEntry { PeerNearMe = peerNearMe, PresenceStatus = peerNearMe.GetPresenceInfo( peerNearMe.PeerEndPoints[0]).PresenceStatus, DisplayString = peerNearMe.Nickname }); } // Add failure message if necessary if (PeerList.Items.Count == 0) { PeerList.Items.Add( new PeerEntry { FIGURE 45-8 c45.indd 16c45.indd 16 11/11/09 5:35:45 PM11/11/09 5:35:45 PM
  17. 17. DisplayString = "No peers found." }); } } As you can see from this example, interacting with the PNM service is made very simple by the classes you have learned about. SUMMARY This chapter demonstrated how to implement peer-to-peer functionality in your applications by using the P2P classes in .NET 4. You have looked at the types of solutions that P2P makes possible and how these solutions are structured, how to use PNRP and PNM, and how to use the types in the System.Net.PeerToPeer and System.Net .PeerToPeer.Collaboration namespaces. You also saw the extremely useful technique of exposing WCF services as P2P endpoints. If you are interested in developing P2P applications, it is well worth investigating PNM further. It is also worth looking at the peer channel, by which WCF services can broadcast communications among multiple clients simultaneously. In the next chapter you look at Message Queuing. Summary ❘ 17 c45.indd 17c45.indd 17 11/11/09 5:35:46 PM11/11/09 5:35:46 PM
  18. 18. c45.indd 18c45.indd 18 11/11/09 5:35:46 PM11/11/09 5:35:46 PM