پیاده سازی چارچوب P2P (نسخه پایتون): | The P2P Framework Implementation (Python version):
The P2P Framework Implementation (Python version):
This page walks through a Python implementation of the P2P framework library itself.
I will assume that you are familiar with Python.
I will also assume you are familiar with the general concepts of socket programming, though you may not necessarily have used the Python networking or threading libraries.
The complete source code may be downloaded here: btpeer.py.
Initializing a peer:
Let us first examine how a peer node is initialized.
As discussed above, the overall operation of a node is managed by the Peer class.
The constructor of the class stores the canonical identifier (name) of the peer, the port on which it listens for connections, and the maximum size of the list of known peers that the node will maintain (this can be set to 0 to allow an unlimited number of peers in the list).
The constructor also initializes several fields whose use is described in the following subsections - shutdown, handlers, and router.
The following definition illustrates the Python code for accomplishing these tasks:
Every peer node performs operations common to traditional network client and server applications.
First, let us walk through the server-related operations, as described in the previous section.
The main loop:
The main loop of a peer begins by setting up a socket that listens for incoming connections from other peers.
To do this, we must (1) create the socket object, (2) set its options, (3) bind it to a port on which to listen for connections, and (4) actually begin listening for connections.
Here is a Python method that accomplishes this, returning the initialized socket:
The first statement creates a socket that will communicate using the IPv4 (AF_INET) protocol with TCP (SOCK_STREAM).
By setting the SO_REUSEADDR option, the port number of the socket will be immediately reusable after the socket is closed (otherwise the operating system may prevent the port from being reused after the server exits, until a certain amount of time has passed).
Then the socket is bound to the specified port and is set up to receive connections.
The backlog parameter indicates how many incoming connections should be queued up.
Having created a server socket, the main loop of a peer loops continously, accepting connections.
When an incoming connection is accepted, the server will have a new socket object used to send and receive data on the connection.
The main loop then calls a separate method to handle communication with this connection in a new thread.
A simple for of the main loop would thus look like this:
In reality, we also need to handle any errors that may occur in the process of accepting a connection, and we need to provide a mechanism so that the loop may somehow be nicely terminated (for example, when the user indicates that the program should exit).
To do this, we set up the server socket to time out every 2 seconds (an arbitrary choice) and make the loop termination condition dependent on a boolean (instance) variable, shutdown.
Also, we set up an exception handler to allow the main loop to be stopped by the user pressing the "Ctrl"+"Break" (or "Ctrl"+"c") keys.
Here, then, is the complete mainloop method.
The debug method will output various messages to an appropriate location - for example, the screen or a log file.
These values are initialized by the constructor for the peer object.
Handling a peer connection:
The handlepeer method takes a newly formed peer connection, reads in a request from it, and dispatches the request to an appropriate handler (function or method) for processing.
The particular handlers and types of requests will be specified by the programmer using this framework to implement a particular protocol.
The handlepeer method simply looks for the appropriate handler for a message, if there is one registered with the peer object, and calls it.
handlepeer begins by encapsulating the socket connection in a PeerConnection object, to allow easy sending/receiving and encoding/decoding of P2P messages in the system.
Then, handlepeer attempts to receive some data from the connection and determine what to do with it:
The handlers field is a dictionary (hash table), mapping message types (4-character strings) to function pointers.
If the message type has a corresponding entry in the dictionary, the function pointer is extracted and invoked, passing it the PeerConnection object and the actual data of the message.
Upon completion of Before returning, handlepeer closes the connection.
Here, then, is the complete definition of the method:Routing and sending messages:
Using the addrouter method, the programmer may register a routing function (or method) with the Peer class to help decide how messages should be forwarded, given a destination peer id.
The routing function should expect as a paremeter the name of a peer (which may not necessarily be present in the list of known peers of the node), and decide which of the known peer the message should be routed to next in order to (hopefully) reach the desired peer.
The routing function should return a tuple of three values: (next-peer-id, host, port) where the host and port are the IP address of the peer identified by next-peer-id.
If the message cannot be routed, the next-peer-id should be None.
The sendtopeer method takes a message type and data, along with a destination peer id, and uses the routing function to decide where to send the message next.
If no routing function has been registered by the programmer, or if the routing function fails for some reason, the method fails.
If the routing function successfully returns the next host/port combination to which the message should be sent, sendtopeer calls the connectandsend method to actually make the connection to the peer, package up, and send the data.
If the programmer desires to receive a response from the next peer before the communication socket is closed, it will be returned by these methods.
The connectandsend method connects and sends a message to a peer at the specified IP address and port.
The Peer class provides methods supporting other fundamental functionalities of a peer node. Briefly, these include: