This article reprinted with permission by java.sun.com
Peer-to-peer (P2P) computing is difficult enough without having to learn a new API. The first version of JXTA introduced us to P2P, but the API was tough to learn, and not easily mapped to our current process. But now, with the release of JXTA 2.0, you get your warm fuzzies back — in the form of a ready-made API to wrap your P2P communications, with a socket-like interface and Java I/O streams.
This article is a tutorial on how to use the new JXTA Socket API in JXTA 2.0. I’ll also demonstrate a simple method for locating a person to chat with using a hash ID. With these two items and a little glue, you can create many different types of useful P2P applications.
In a Nutshell: Understanding P2P and JXTA
Are you new to P2P and JXTA? Simply put, P2P is just the interaction of two or more computers. However, the story is much more complex than a few cables. Firewalls, DHCP, and NAT devices hide the identity and addresses of computers and block direct communications. And since there are millions of computers and devices connected to the Internet, locating a specific device with a specific resource is not obvious and doubly complex because most devices are un-addressable and firewalled. JXTA and other P2P protocols are designed to solve these problems.
JXTA is a P2P protocol specification that includes addressing, routing, messaging, and other services. JXTA creates a virtual network that overlays the mess of un-addressable and blocked computers with a uniform address space and network-level functions for discovering, grouping, and communication between PCs and devices. JXTA — because it is a protocol — is available in several languages and platforms, including the Java language and C++.
Why P2P now? Hasn’t P2P existed since two computers were networked? Today’s massive numbers of connected devices with idle resources is a key reason P2P is currently on the rise. P2P computing can reduce your need for a large support infrastructure by substituting PCs in homes and businesses as resources, rather than centralized servers. Turning client-server programming upside down is not always straightforward, but the good news is that the techniques and API are getting simpler — as you will see in the remainder of this art
The Chat Application
The example I’ll be using is very simple; Chat is the equivalent of Hello World for P2P. Here’s all we’re going to do:
- Initialize the JXTA platform
- Create a server socket to wait for connections
- Process incoming connections, or initiate a connection to another computer
- Once a connection is established, send messages back and forth
Simple? In a way, it is, but JXTA (as demonstrated in this example) will let you accomplish a very complex task: Computers can “chat” all over the world, without the need for a server.
The JXTA Socket API
Now that you understand a little about our application, take a look at the classes in the JXTA socket package:
JxtaServerSocket: Server socket that waits for connections from clients.JxtaSocket: Socket class used to create the I/O streams for both clients and servers.JxtaSocketInputStream: Extension of the InputStream abstract class.JxtaSocketOutputStream: Extension of the OutputStream abstract class.
These classes should look familiar, since they’re very similar to what is in the Java I/O package. The sequence of events is similar to sockets in the java.io package, too. We start with the creation of a JxtaServerSocket on the computer waiting for others to connect to it. A client (the caller) computer uses the JxtaSocket class to connect. Both computers create the corresponding I/O streams using JxtaSocket, and begin talking with each other. There is a bit more to do first, however. Remember that JXTA does not use IP addresses. So it’s time to initialize the JXTA network and create a JXTA address.
Deeper into JXTA Sockets
The classes JxtaServerSocket and JxtaSocket are similar to the Socket and SocketServer classes in the Java I/O package. The JxtaServerSocket class is used to wait for connections. JxtaSocket is used to access the InputStream and OutputStream objects when a client connects to a JxtaServerSocket (or for a client to access these stream objects when connecting to a server). Put most simply, the socket classes let two computers communicate.
In addition, the JxtaServerSocket can accept multiple connections from multiple computers and create separate I/O streams for each one. This allows multiple computers to be connected and talking to a single computer, with new I/O channels allocated for each inbound connection.
Figure 1 shows a user peer using JxtaSocket to contact a waiting JxtaServerSocket.

Figure 1: User 1 connects to User 2.
Figure 2 shows three users in our P2P chat network. Each user has a JxtaServerSocket to wait for connections. In the peer-to-peer space, any one peer is usually both a client and a server. This duality exists because of the distributed nature of most P2P applications. In our chat example, the application is truly symmetric. The chat application is always a server and becomes a client when starting a chat with another user (such as when User 1 contacts User 2 in Figure 2).

Figure 2: All users have a JxtaServerSocket listening to their specific ID.
Once the socket is connected, each peer has two streams. One is based on OutputStream and the other is InputStream. Both streams are connected from the input of one to the output of the other. The chat application is now ready to pass messages from either computer to the other.
Starting JXTA
Let’s now look at the example code. As mentioned, JXTA creates a virtual overlay network, so any application participating in the network needs to initialize its node, connect to the network, and announce its presence to others in the network. The initialization process is accomplished by joining the net peer group. The net group is the default groups all peers join when they connect to the JXTA network.
Line 2 of Listing 1 calls newNetPeerGroup(). Once called, several threads are started in the JXTA platform to initialize connections to other peers.
Note: Here is the code. The bulk of the classes are not shown, and some statements — including exceptions, logging, and other nonessential code — have been removed to improve readability.
Listing 1: ChatManager.startJXTA() used to initialize JXTA
-==-
In addition to starting the JXTA platform, we also need to create a context for us to operate within. Line 3 creates a peer group ID using a utility class which converts a couple of strings (“hello world application” and “chat”) to an MD5 checksum that is in turn is converted to a PeerGroupID. On line 4, the PeerGroupID creates a peer group which we will operate within by calling another of our utility classes — PeerGroupTool. This group ensures that we have an isolated ID space we can control. Peer groups in JXTA create a context that limits messages and the scope of services to just the peers that are members of the particular group. This scoping is similar to a subnet, but can include any PC at any network address. Scoping also acts as a barrier, allowing sockets to connect only within the same group.
After calling this method, we are a part of the JXTA virtual network and ready to set up our socket.
The Listening Peer
To create a socket, we need an ID that can be used to represent the chat user. In this case, we will create an ID that is the MD5 checksum of a name specified by the user. The ID is used in a similar way to an IP and port number for a traditional socket.
At line 2 of Listing 2, we specify the type of the pipe. The example uses the simple unicast type because we aren’t worried about security. The pipe type can be either UnicastType or UnicastSecureType. These are the unencrypted and encrypted versions of messaging in JXTA. (The PropagateType is not available in this version of the JXTA socket API).
In line 3, we create an ID based on the name, the type of pipe, and a string for a function, in this case chat. We use the string to create a checksum that is unique enough to avoid collisions. We are combining the user name, a function name, and the pipe type to avoid collisions with other pipes we may add to the application. This is the equivalent of IP address and port number. The ID will be discussed later.
The class ConnectionServer is created on line 4. This class wraps our socket code in a thread to allow the program to continue executing while we wait for another computer to connect. In line 5, we add a listener to be called when a computer connects. Finally, line 6 starts the thread.
Listing 2: addServer() Used to start a server to wait for connections.
-==-
Waiting for Connections
The next thing to do is start the JxtaServerSocket. This is simple enough, using the peer group and pipe advertisement as shown in the following code fragment from the ConnectionServer class’s constructor.
-==-
Because this is a server socket, we need to wait for client peers to connect. Line 5 is where the JxtaServerSocket object is asked to block, waiting for a new connection. In the following lines, the registered listeners are called with the JxtaSocket class, which is the handle to the connection.
Listing 3: ConnectionServer.run() thread accepting the connections and calling listeners.
-==-
The Chat Caller
The creation of the socket from the client side requires the same initial steps as for the server. We use the same initialization of JXTA, and the same strings to designate the peer group and pipe ID (line 3 of Listing 4) for the socket creation. In fact, all the code is the same for both the server and the client. Every peer starts a socket server and every peer can initiate a client-side connection. This may sound strange, but it’s logical: any chatter can start the conversation with any peer that is listening.
On line 4, we create a pipe advertisement in the same way we did for the server socket. These advertisements must match, or the client peer will not be able to find the server. Line 5 initializes a Swing GUI window to display the chat messages. The ChatSession class on line 6 uses the window object as a parameter, since the window directly echoes messages and gets strings to send.
Listing 4: connectToServer()
-==-
Connecting and Waiting for Data
With everything set up, we can now connect to a server. The code is also set up to process an incoming connection. The code in Listing 5 sets up the I/O streams for either case. On line 3, we check to see if the value of jxtaSocket has been initialized. If it is null, we assume the connection is from a client to a serving peer and create a new JxtaSocket object. If jxtaSocket is not null, then we get its value from the socket server.
Remember that the jxtaSocket is a handle to both the input and output streams. We get a reference to these streams in lines 8 and 9. Now we loop on the input stream. On line 14, the stream blocks on the input stream, waiting for data. Note that this is executing in its own thread to prevent the system from blocking as we process the stream. On line 16, we call the chat window with the text.
Listing 5: ReaderThread.run(): Setup of connection and loop waiting on data.
-==-
Creating MD5 Checksum IDs
Earlier, we created pipe and group IDs from MD5 checksums. As mentioned previously, a pipe ID is similar to an IP address and port number. We use the pipe ID because the address and port may not be visible due to a NAT or firewall. In addition, the IP address could be changed at any time by a DHCP server. The pipe ID signifies a logical address for connection, regardless of the true machine physical address or its addressability.
When there are multiple peers with the same pipe ID, the peer would be chosen randomly. This might seem odd at first, but some situations in P2P call for a random use of resources. A random ID is good if it’s not important which computer you connect to. In our case, however, the intent is to connect to a very specific peer for a chat. For our pipe ID, we will be careful to create a unique ID. We’ll use the user’s unique email address, because we need a way to contact a specific person, and an ID that is known and predictable.
A checksum or hash is used to hide the email address and to ensure that our ID will work for a large or small email address string. We always get the same length encoding for any length string. We are using MD5 hashing in this example because we can fit it into the UUID ID, which is the reference ID in JXTA.
There are several ways to create a predictable ID. We can create a specific ID using BinaryID or the seeded UUID implementation of the PipeID. We will use the seeded UUID, because at the time of this writing, BinaryID is available, but not in the current release.
To create a seeded UUID, you create a byte array that represents the equivalent ID. The constructor creates a UUID and merges the binary value to create a valid UUID.
The method in Listing 6 creates the MD5 hash from two strings, which it concatenates.
Listing 6: generateHash(): Setup of connection and loop waiting on data.
-==-
In Listings 7 and 8, the PeerGroupID and PipeID are created with the hash code in the byte array.
Listing 7: createPeerGroupID(): Create a PipeID using the MD5 hash of a string.
-==-
Listing 8: createPipeID(): Create a PipeID using the MD5 hash of a string.
-==-
In the future, instead of the MD5 checksum, we could use the BinaryID and SHA-1 checksum which (as mentioned earlier) will be available in the JXTA 2.0.X release (these are available in the development release, but not the 2.0 stable release). The UUID-based IDs used in these examples are only 128 bits, while the BinaryID can be as large as 255 bytes (2040 bits), allowing for a larger unique ID.
Overview of the P2P Chat Application
Our chat application is simple. The point of the application is just to demonstrate how to use JxtaSocket and its accompanying classes, so there are few bells and whistles. Like any socket-like application, we have both a client and a server that waits for connections from the client. But unlike many client-server applications, components of both are used, since any peer can be both client and server, depending on who initiates a connection.
The design defines the following classes:
ChatManager: Class for connecting to the virtual P2P JXTA network, and for creating a chat server and chat sessions.ChatManagerFrame: Frame for creating chat sessions.ChatWindow: A simple Swing window for the user to see input and create outputs.ChatSession: Core code that starts a chat connection and then handles the send/receive on I/O streams created.ConnectionServer: This thread waits on the socket. As other computers connect to the socket, this class creates a new class session.ChatListener: Used as an interface between the chat session and the chat window.ChatSendListener: Interface to link the GUI with the session. This has a send method used to pass the string to the session for transmission.ConnectionListener: Interface to announce that a new socket connection was created.PeerGroupTool: A class of utility methods that create the peer group.
Figure 3 shows the classes in the application. The classes in the JXTA socket package are in blue.

Click to enlarge
How JXTA Works: Putting It All Together
The big difference between the java.io package and JXTA’s is that we don’t use an Internet address or a port number, since we are connecting to personal computers that are not always addressable (or their address changes because of DHCP or they could even be laptops moving between networks).
It appears that computers are all connected via the web of the Internet. In truth, there are many barriers to direct connection. The obvious barriers are firewalls and NATs. The firewall usually blocks inbound traffic. We can still use the web, however, since the HTTP protocol is filtered so that we receive data based on requests. For each outbound request, the firewall allows in the corresponding response. The NAT presents a similar problem by hiding the IP address of computers in a local net and replacing it with one external address. The NAT is used by most companies and Internet service providers. Most home network connections are through one or more NAT devices.
What about security? The only conversations that can occur when the user is behind the firewall is when the user (or software) initiates them. This is identical to a browser application. The layer of security provided by the firewall is not breached.
How do any two computers isolated by firewalls talk to each other? The key is that a third peer is involved that acts as a relay. Each computer contacts the relay to collect messages or leave them to be accessed by the other computer. This is identical to what happens with HTTP tunneling, but with one little twist: The relay computer is just another PC someplace on the Internet. And the significance is that you don’t need to have a server to have two computers talk to each other.
In addition to HTTP, JXTA also supports TCP. If peers can access each other directly, there is no need for a relay peer. This means, of course, a much faster throughput.
Firewalls are not the only problem. DHCP can also cause problems by periodically changing the IP address of the computer. For the JXTA socket API, there is an additional ID called a pipe ID. The pipe ID acts as the socket’s virtual address and port number. JXTA’s routing system connects computers that are attempting to connect with the same pipe ID. For example, if a peer opens the JxtaServerSocket and listens for an ID of 5, and another computer is looking for a pipe ID of 5, the router connects the two. The routing and connecting of the two computers is too complex to describe here, but the result is that the conversation can begin.
Here is a summary of what happens to connect two computers:
- Computer A opens a server socket with a pipe ID of X.
- Computer B opens a client socket with an ID of X.
- JXTA creates a route between A and B.
The routing between A and B calculates what relays to use, or directly connects the two computers on the same network. To learn more about the magic of JXTA, refer to the JXTA documentation and books at the www.jxta.org web site.
Summary and Final Thoughts
In this article, you’ve seen how to create a simple socket application running in JXTA’s P2P network. I also covered a simple way to address these sockets, using an MD5 checksum, which replaces the notion of an IP and port number.
As JXTA technology continues to mature, we should expect to see a closer integration of JXTA with standard J2SE Java APIs. JXTA 2.0 is now easier, faster, stable, and ready for primetime. It’s also already being used for many commercial applications.
JXTA is a community and a new world we can explore together, so email me and let me know about the JXTA applications you’re working on.
Copyright (c) 2002-2003 Sun Microsystems, Inc. All Rights Reserved.
Daniel Brookshier is the lead author and editor of JXTA: Java P2P Programming. He is a hands-on Java architect concentrating on JXTA, J2EE, Swing, and mentoring. Daniel is an independent consultant with clients in multiple industries, including such well known companies as Boeing, Ericsson, and Wal-Mart. His two personal Web sites are http://people.jxta.org/turbogeek/ and www.cluck.com
