ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Introducing Nonblocking Sockets

by Giuseppe Naccarato
09/04/2002

Nonblocking sockets, introduced in Java 2 Standard Edition 1.4, allow net communication between applications without blocking the processes using the sockets. In this article, I will show in detail what a nonblocking socket is, how it works, and in which contexts it can be useful.

As of Java 1.4, a programmer can use a brand-new set of APIs for I/O operations. This is the result of JSR 51, which started in January 2000 and has been available to programmers since Java 1.4 beta. Some of the most important new features in Java 1.4 deal with subjects such as high performance read/write operations on both files and sockets, regular expressions, decoding/encoding character sets, in-memory mapping, and locking files. In this article, I will discuss one particular new concept -- the new I/O API: nonblocking sockets.

A nonblocking socket allows input/output operation on a channel without blocking the processes using it. I'm talking about asynchronous high-performance read/write operations that, as you will see, turn upside-down the techniques for designing and developing socked-based applications.

Java developers might ask: why introducing a new technology to handle sockets? What's wrong with the Java 1.3.x sockets? Suppose you would like to implement a server accepting diverse client connections. Suppose, as well, that you would like the server to be able to process multiple requests simultaneously. Using Java 1.3.x, you have two choices to develop such a server:

  • Implement a multithread server that manually handles a thread for each connection.
  • Using an external third-party module.

Both solutions work, but adopting the first one -- the whole thread-management solution, with related concurrency and conflict troubles -- has to be developed by programmer. The second solution may cost money, and it makes the application dependent on a non-JDK external module. By means of the nonblocking socket, you can implement a nonblocking server without directly managing threads or resorting to external modules.

Buffer

Related Reading

Java NIO
By Ron Hitchens

Before we can face the nonblocking socket issue, we have to spend some words about a new Java 1.4 class: java.nio.Buffer. A Buffer instance is merely a limited container of primitive data. It is limited because it can contain a limited number of bytes; in other words, it is not a container, like a Vector or an ArrayList, which theoretically have no limit. In addition, a Buffer instance can only contain items belonging to a Java base type, such as int, char, double, boolean, and so on.

The Buffer class is an abstract class having seven known implementations, one for each base type:

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

In nonblocking sockets programming, and in general in all contexts where the new I/O system works, it's very important to figure out how Buffer objects work. This is because the new socket channels use Buffer objects to transfer data across the net.

You can create a new Buffer instance by using one of its static methods: allocate, allocateDirect, or wrap. In the following code sample, three Buffer objects will be instantiated in three different ways:

ByteBuffer buffer1 = ByteBuffer.allocate(1024);
ByteBuffer buffer2 = ByteBuffer.allocateDirect(1024);
ByteBuffer buffer3 = ByteBuffer.wrap(new String("hello").getBytes());

The first two lines of this code fragment create two ByteBuffer objects, which can each contain 1024 bytes. The allocate and allocateDirect methods work in the same way; the only difference is that the second one directly uses the operating system to allocate the buffer; thus it should provide faster access. Unfortunately, not all virtual machines support the direct-allocation method. The third line uses the wrap method. It creates a ByteBuffer object, which contains the bytes composing the string "hello."

Working with Buffer objects is more or less the same as working with streams. The "current position" concept is an important thing to figure out if you want to deal with the Buffer objects properly. At any time, a buffer has a current position indicating an item. Automatically, after each read or write operation, the current position indicates the next item in the buffer.

To write something to the buffer, you can use the put method:

// Writing on a buffer
IntBuffer buffer = IntBuffer.allocate(10);
  for (int i=0; i < buffer.capacity(); i++) {
  buffer.put(i);
}

The code fragment creates a buffer that can contain 10 integer values. Then, the numbers from 0 to 9 are put in the buffer. As you can see, I use the capacity method to get the capacity of the buffer.

To read the contents of a buffer, you can proceed in the following way:

// Reading from a buffer
buffer.position(0);
while (buffer.hasRemaining()) {
  int i = buffer.get();
  System.out.println("i="+i);
}

Invoking the position method, you can set the current position to 0; that is, at the beginning of the buffer. The hasRemaining method returns true, whether or not there are still elements between the current position and the limit of the buffer. The code inside of the while instruction reads the items by calling the get method and shows them on the console.

It is very important to understand the difference between the limit and the capacity of the buffer. The capacity is the maximum number of items the buffer can contain; the limit is a value, somewhere between 0 and the capacity, representing an arbitrary limitation for the buffer set by using the limit or the flip methods. Take a look at the following example:

// Sample of using flip
buffer.position(5);
buffer.flip();
while (buffer.hasRemaining()) {
  int i = buffer.get();
  System.out.println("i="+i);
}

The current position is set to 5 by the position method. The flip method works like this: it sets the limit to the current position, which is 5; then, it sets the current position again to 0. Therefore, the while loop will scan only the first five elements, because the flip method has set the new limit to 5. Therefore, the shown numbers will be 0, 1, 2, 3, and 4.

Another important method of the Buffer class is clear. It sets the current position to 0 and the limit to the capacity of the buffer. Basically, the clear method annuls the effects provided by previous flip (or limit) invocations. Consider this example:

// Sample of using clear
buffer.clear();
while (buffer.hasRemaining()) {
  int i = buffer.get();
  System.out.println("i="+i);
}

The code above will show the numbers from 0 to 9, independently of the current position and the limit of the buffer.

Pages: 1, 2, 3

Next Pagearrow