NIO继续研究 Socket
程序员文章站
2022-07-12 16:38:04
...
例子程序来自于The Java Developers Almanac 1.4
http://javaalmanac.com/?l=ex
Creating a Non-Blocking Socket
Reading from a SocketChannel
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.
Writing to a SocketChannel
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.
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.
e178. Accepting a Connection on a ServerSocketChannel
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.
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.
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 }