例子程序来自于
The Java Developers Almanac 1.4
http://javaalmanac.com/?l=ex
Creating a Non-Blocking Socket
// Creates a non-blocking socket channel for the specified host name and port.
// connect(); is called on the new channel before it is returned.
public static SocketChannel createSocketChannel(String hostName, int port); throws IOException {
// Create a non-blocking socket channel
SocketChannel sChannel = SocketChannel.open();;
sChannel.configureBlocking(false);;
// Send a connection request to the server; this method is non-blocking
sChannel.connect(new InetSocketAddress(hostName, port););;
return sChannel;
}
// Create a non-blocking socket and check for connections
try {
// Create a non-blocking socket channel on port 80
SocketChannel sChannel = createSocketChannel("hostname.com", 80);;
// Before the socket is usable, the connection must be completed
// by calling finishConnect();, which is non-blocking
while (!sChannel.finishConnect();); {
// Do something else
}
// Socket channel is now ready to use
} catch (IOException e); {
}
Reading from a SocketChannel
// Create a direct buffer to get bytes from socket.
// Direct buffers should be long-lived and be reused as much as possible.
ByteBuffer buf = ByteBuffer.allocateDirect(1024);;
try {
// Clear the buffer and read bytes from socket
buf.clear();;
int numBytesRead = socketChannel.read(buf);;
if (numBytesRead == -1); {
// No more bytes can be read from the channel
socketChannel.close();;
} else {
// To read the bytes, flip the buffer
buf.flip();;
// Read the bytes from the buffer ...;
// see e159 Getting Bytes from a ByteBuffer
}
} catch (IOException e); {
// Connection may have been closed
}
Getting Bytes from a ByteBuffer
A ByteBuffer has a capacity that determines how many bytes it contains. This capacity can never change. Any byte in the buffer can be retrieved using the absolute version of get(), which takes an index in the range [0..capacity-1].
The bytes in a ByteBuffer can also be retrieved using the relative version of get(), which uses the position and limit properties of the buffer. In particular, this version of get() retrieves the byte at the position and advances the position by one. get() cannot retrieve bytes past the limit (even though the limit might be less than the capacity). The position is always <= limit and limit is always <= capacity.
// Create an empty ByteBuffer with a 10 byte capacity
ByteBuffer bbuf = ByteBuffer.allocate(10);;
// Get the ByteBuffer's capacity
int capacity = bbuf.capacity();; // 10
// Use the absolute get();.
// This method does not affect the position.
byte b = bbuf.get(5);; // position=0
// Set the position
bbuf.position(5);;
// Use the relative get();
b = bbuf.get();;
// Get the new position
int pos = bbuf.position();; // 6
// Get remaining byte count
int rem = bbuf.remaining();; // 4
// Set the limit
bbuf.limit(7);; // remaining=1
// This convenience method sets the position to 0
bbuf.rewind();; // remaining=7
Writing to a SocketChannel
// Create a direct buffer to get bytes from socket.
// Direct buffers should be long-lived and be reused as much as possible.
ByteBuffer buf = ByteBuffer.allocateDirect(1024);;
try {
// Fill the buffer with the bytes to write;
// see e160 Putting Bytes into a ByteBuffer
buf.put((byte);0xFF);;
// Prepare the buffer for reading by the socket
buf.flip();;
// Write bytes
int numBytesWritten = socketChannel.write(buf);;
} catch (IOException e); {
// Connection may have been closed
}
Using a Selector to Manage Non-Blocking Socket
Although you could poll each non-blocking socket for events, a more convenient and efficient method is to use a selector to manage the channels. The selector efficiently monitors the channels for changes and reports the events through a single method call.
The first step is to register a channel with a selector. The registration process yields an object called a selection key which identifies the selector/socket channel pair (a channel could be registered with another selector for different events). When an event occurs on a channel, the selector returns the selection key for that channel. The selection key also contains the type of event that occurred.
This example creates two sockets and registers them with a selector. The example then uses the selector to listen for events.
See also e179 Using a Selector to Manage Non-Blocking Server Sockets.
// Create a selector and register two socket channels
Selector selector = null;
try {
// Create the selector
selector = Selector.open();;
// Create two non-blocking sockets. This method is implemented in
// e173 Creating a Non-Blocking Socket.
SocketChannel sChannel1 = createSocketChannel("hostname.com", 80);;
SocketChannel sChannel2 = createSocketChannel("hostname.com", 80);;
// Register the channel with selector, listening for all events
sChannel1.register(selector, sChannel1.validOps(););;
sChannel2.register(selector, sChannel1.validOps(););;
} catch (IOException e); {
}
// Wait for events
while (true); {
try {
// Wait for an event
selector.select();;
} catch (IOException e); {
// Handle error with selector
break;
}
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys();.iterator();;
// Process each key at a time
while (it.hasNext();); {
// Get the selection key
SelectionKey selKey = (SelectionKey);it.next();;
// Remove it from the list to indicate that it is being processed
it.remove();;
try {
processSelectionKey(selKey);;
} catch (IOException e); {
// Handle error with channel and unregister
selKey.cancel();;
}
}
}
public void processSelectionKey(SelectionKey selKey); throws IOException {
// Since the ready operations are cumulative,
// need to check readiness for each operation
if (selKey.isValid(); && selKey.isConnectable();); {
// Get channel with connection request
SocketChannel sChannel = (SocketChannel);selKey.channel();;
boolean success = sChannel.finishConnect();;
if (!success); {
// An error occurred; handle it
// Unregister the channel with this selector
selKey.cancel();;
}
}
if (selKey.isValid(); && selKey.isReadable();); {
// Get channel with bytes to read
SocketChannel sChannel = (SocketChannel);selKey.channel();;
// See e174 Reading from a SocketChannel
}
if (selKey.isValid(); && selKey.isWritable();); {
// Get channel that's ready for more bytes
SocketChannel sChannel = (SocketChannel);selKey.channel();;
// See e175 Writing to a SocketChannel
}
}
Creating a Non-Blocking Server Socket
This example shows how to create a non-blocking server socket. A non-blocking server socket requires a server socket channel.
// Create a non-blocking server socket and check for connections
try {
// Create a non-blocking server socket channel on port 80
ServerSocketChannel ssChannel = ServerSocketChannel.open();;
ssChannel.configureBlocking(false);;
int port = 80;
ssChannel.socket();.bind(new InetSocketAddress(port););;
// See e178 Accepting a Connection on a ServerSocketChannel
// for an example of accepting a connection request
} catch (IOException e); {
}
e178. Accepting a Connection on a ServerSocketChannel
// Get port that received the connection request; this information
// might be useful in determining how to handle the connection
int localPort = serverSocketChannel.socket();.getLocalPort();;
try {
// Accept the connection request.
// If serverSocketChannel is blocking, this method blocks.
// The returned channel is in blocking mode.
SocketChannel sChannel = serverSocketChannel.accept();;
// If serverSocketChannel is non-blocking, sChannel may be null
if (sChannel == null); {
// There were no pending connection requests; try again later.
// To be notified of connection requests,
// see e179 Using a Selector to Manage Non-Blocking Server Sockets.
} else {
// Use the socket channel to communicate with the client
// See e176 Using a Selector to Manage Non-Blocking Sockets.
}
} catch (IOException e); {
}
e179. Using a Selector to Manage Non-Blocking Server Sockets
For more information about selectors, see e176 Using a Selector to Manage Non-Blocking Sockets.
This example creates two server sockets and registers them with a selector. The example then uses the selector to listen for events.
try {
// Create the selector
Selector selector = Selector.open();;
// Create two non-blocking server sockets on 80 and 81
ServerSocketChannel ssChannel1 = ServerSocketChannel.open();;
ssChannel1.configureBlocking(false);;
ssChannel1.socket();.bind(new InetSocketAddress(80););;
ServerSocketChannel ssChannel2 = ServerSocketChannel.open();;
ssChannel2.configureBlocking(false);;
ssChannel2.socket();.bind(new InetSocketAddress(81););;
// Register both channels with selector
ssChannel1.register(selector, SelectionKey.OP_ACCEPT);;
ssChannel2.register(selector, SelectionKey.OP_ACCEPT);;
while (true); {
// Wait for an event
selector.select();;
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys();.iterator();;
// Process each key
while (it.hasNext();); {
// Get the selection key
SelectionKey selKey = (SelectionKey);it.next();;
// Remove it from the list to indicate that it is being processed
it.remove();;
// Check if it's a connection request
if (selKey.isAcceptable();); {
// Get channel with connection request
ServerSocketChannel ssChannel = (ServerSocketChannel);selKey.channel();;
// See e178 Accepting a Connection on a ServerSocketChannel
// for an example of accepting a connection request
}
}
}
} catch (IOException e); {
}
e180. Detecting When a Non-Blocking Socket Is Closed by the Remote Host
The only way to detect that the remote host has closed the connection is to attempt to read or write from the connection. If the remote host properly closed the connection, read() will return -1. If the connection was not terminated normally, read() and write() will throw an exception.
When using a selector to process events from a non-blocking socket, the selector will try to return an OP_READ or OP_WRITE event if the remote host has closed the socket.
try {
// Read from socket
int numBytesRead = socketChannel.read(buf);;
if (numBytesRead == -1); {
// No more bytes can be read from the channel
socketChannel.close();;
} else {
// Read the bytes from the buffer
}
} catch (IOException e); {
// Connection may have been closed
}
try {
// Write to socket
int numBytesWritten = socketChannel.write(buf);;
} catch (IOException e); {
// Connection may have been closed
}