欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

NIO继续研究 Socket 

程序员文章站 2022-07-12 16:38:04
...
例子程序来自于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
    }
相关标签: Socket