Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Getting started with new I/O (NIO)

Greg Travis (mito@panix.com), Java programmer and technology writer
Greg Travis is a freelance Java programmer and technology writer living in New York City. Greg started his programming career in 1992, spending three years in the world of high-end PC games. In 1995, he joined EarthWeb, where he began developing new technologies with the Java programming language. Since 1997, Greg has been a consultant in a variety of Web technologies, specializing in real-time graphics and sound. His interests include algorithm optimization, programming language design, signal processing (with emphasis on music), and real-time 3D graphics. Other articles by Greg can be found on his personal Web page. He is also the author of JDK 1.4 Tutorial, published by Manning Publications.

For technical questions or comments about the content of this tutorial, contact Greg Travis at mito@panix.com.

Summary:  The new input/output (NIO) library, introduced with JDK 1.4, provides high-speed, block-oriented I/O in standard Java code. This hands-on tutorial covers the NIO library in great detail, from the high-level concepts to under-the-hood programming detail. You'll learn about crucial I/O elements like buffers and channels, and examine how standard I/O works in the updated library. You'll also learn about things you can do only with NIO, such as asynchronous I/O and direct buffers.

Date:  09 Jul 2003
Level:  Introductory PDF:  A4 and Letter (145 KB | 34 pages)Get Adobe® Reader®

Activity:  68170 views
Comments:  

Networking and asynchronous I/O

Networking and asynchronous I/O overview

Networking is an excellent foundation for learning about asynchronous I/O, which is of course essential knowledge for anyone doing input/output procedures in the Java language. Networking in NIO isn't much different from any other operation in NIO -- it relies on channels and buffers, and you acquire the channels from the usual InputStream s and OutputStream s.

In this section we'll start with the fundamentals of asynchronous I/O -- what it is and what it is not -- and then move on to a more hands-on, procedural example.


Asynchronous I/O

Asynchronous I/O is a method for reading and writing data without blocking. Normally, when your code makes a read() call, the code blocks until there is data to be read. Likewise, a write() call will block until the data can be written.

Asynchronous I/O calls, on the other hand, do not block. Instead, you register your interest in a particular I/O event -- the arrival of readable data, a new socket connection, and so on -- and the system tells you when such an event occurs.

One of the advantages of asynchronous I/O is that it lets you do I/O from a great many inputs and outputs at the same time. Synchronous programs often have to resort to polling, or to the creation of many, many threads, to deal with lots of connections. With asynchronous I/O, you can listen for I/O events on an arbitrary number of channels, without polling and without extra threads.

We'll see asynchronous I/O in action by examining an example program called MultiPortEcho.java. This program is like the traditional echo server, which takes network connections and echoes back to them any data they might send. However, it has the added feature that it can listen on multiple ports at the same time, and deal with connections from all of those ports. And it does it all in a single thread.


Selectors

The explanation in this section corresponds to the implementation of the go() method in the source code for MultiPortEcho, so take a look at the source for a fuller picture of what is going on.

The central object in asynchronous I/O is called the Selector. A Selector is where you register your interest in various I/O events, and it is the object that tells you when those events occur.

So, the first thing we need to do is create a Selector:

Selector selector = Selector.open();

Later on, we will call the register() method on various channel objects, in order to register our interest in I/O events happening inside those objects. The first argument to register() is always the Selector.


Opening a ServerSocketChannel

In order to receive connections, we need a ServerSocketChannel. In fact, we need one for each of the ports on which we are going to listen. For each of the ports, we open a ServerSocketChannel, as shown here:

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking( false );

ServerSocket ss = ssc.socket();
InetSocketAddress address = new InetSocketAddress( ports[i] );
ss.bind( address );

The first line creates a new ServerSocketChannel and the last three lines bind it to the given port. The second line sets the ServerSocketChannel to be non-blocking. We must call this method on every socket channel that we're using; otherwise asynchronous I/O won't work.


Selection keys

Our next step is to register the newly opened ServerSocketChannels with our Selector. We do this using the ServerSocketChannel.register() method, as shown below:

SelectionKey key = ssc.register( selector, SelectionKey.OP_ACCEPT );

The first argument to register() is always the Selector. The second argument, OP_ACCEPT, here specifies that we want to listen for accept events -- that is, the events that occur when a new connection is made. This is the only kind of event that is appropriate for a ServerSocketChannel.

Note the return value of the call to register(). A SelectionKey represents this registration of this channel with this Selector. When a Selector notifies you of an incoming event, it does this by supplying the SelectionKey that corresponds to that event. The SelectionKey can also be used to de-register the channel.


The inner loop

Now that we have registered our interest in some I/O events, we enter the main loop. Just about every program that uses Selectors uses an inner loop much like this one:

int num = selector.select();

Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();

while (it.hasNext()) {
     SelectionKey key = (SelectionKey)it.next();
     // ... deal with I/O event ...
}

First, we call the select() method of our Selector. This method blocks until at least one of the registered events occurs. When one or more events occur, the select() method returns the number of events that occurred.

Next, we call the Selector 's selectedKeys() method, which returns a Set of the SelectionKey objects for which events have occurred.

We process the events by iterating through the SelectionKeys and dealing with each one in turn. For each SelectionKey, you must determine what I/O event has happened and which I/O objects have been impacted by that event.


Listening for new connections

At this point in the execution of our program, we've only registered ServerSocketChannel s, and we have only registered them for "accept" events. To confirm this, we call the readyOps() method on our SelectionKey and check to see what kind of event has occurred:

if ((key.readyOps() & SelectionKey.OP_ACCEPT)
     == SelectionKey.OP_ACCEPT) {

     // Accept the new connection
     // ...
}

Sure enough, the readOps() method tells us that the event is a new connection.


Accepting a new connection

Because we know there is an incoming connection waiting on this server socket, we can safely accept it; that is, without fear that the accept() operation will block:

ServerSocketChannel ssc = (ServerSocketChannel)key.channel();
SocketChannel sc = ssc.accept();

Our next step is to configure the newly-connected SocketChannel to be non-blocking. And because the purpose of accepting this connection is to read data from the socket, we must also register the SocketChannel with our Selector, as shown below:

sc.configureBlocking( false );
SelectionKey newKey = sc.register( selector, SelectionKey.OP_READ );

Note that we've registered the SocketChannel for reading rather than accepting new connections, using the OP_READ argument to register().


Removing the processed SelectionKey

Having processed the SelectionKey, we're almost ready to return to the main loop. But first we must remove the processed SelectionKey from the set of selected keys. If we do not remove the processed key, it will still be present as an activated key in the main set, which would lead us to attempt to process it again. We call the iterator's remove() method to remove the processed SelectionKey:

it.remove();

Now we're set to return to the main loop and receive incoming data (or an incoming I/O event) on one of our sockets.


Incoming I/O

When data arrives from one of the sockets, it triggers an I/O event. This causes the call to Selector.select(), in our main loop, to return with an I/O event or events. This time, the SelectionKey will be marked as an OP_READ event, as shown below:

} else if ((key.readyOps() & SelectionKey.OP_READ)
     == SelectionKey.OP_READ) {
     // Read the data
     SocketChannel sc = (SocketChannel)key.channel();
     // ...
}

As before, we get the channel in which the I/O event occurred and process it. In this case, because this is an echo server, we just want to read the data from the socket and send it right back. See the source code (MultiPortEcho.java) in Resources for details on this process.


Back to the main loop

Each time we return to the main loop we call the select() method on our Selector, and we get a set of SelectionKey s. Each key represents an I/O event. We process the events, remove the SelectionKey s from the selected set, and go back to the top of the main loop.

This program is a bit simplistic, since it aims only to demonstrate the techniques involved in asynchronous I/O. In a real application, you would need to deal with closed channels by removing them from the Selector. And you would probably want to use more than one thread. This program can get away with a single thread because it's only a demo, but in a real-world scenario it might make more sense to create a pool of threads for taking care of the time-consuming portions of I/O event processing.

9 of 13 | Previous | Next

Comments



Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=482400
TutorialTitle=Getting started with new I/O (NIO)
publish-date=07092003
author1-email=mito@panix.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).