Java NIO:OP_CONNECT从未在客户机中触发,即使连接已被服务器接受

Java NIO:OP_CONNECT从未在客户机中触发,即使连接已被服务器接受,java,networking,nio,Java,Networking,Nio,我正在学习JavaNIO,并尝试使用选择器编写一个简单的客户机/服务器应用程序。到目前为止,我已经 一个PwpConnectionManager类,既充当侦听传入连接的服务器,也充当客户端 与远程对等方建立连接。要连接到的远程对等方是通过connectTo()方法添加的 虽然检测和接受传入连接似乎有效,但我无法在以下情况下触发OP_CONNECT事件: 正在尝试连接到远程对等点。我希望选择键和selectedKey.isConnectable()在 已成功建立远程连接。这永远不会发生 为了说明这

我正在学习JavaNIO,并尝试使用选择器编写一个简单的客户机/服务器应用程序。到目前为止,我已经 一个PwpConnectionManager类,既充当侦听传入连接的服务器,也充当客户端 与远程对等方建立连接。要连接到的远程对等方是通过connectTo()方法添加的

虽然检测和接受传入连接似乎有效,但我无法在以下情况下触发OP_CONNECT事件: 正在尝试连接到远程对等点。我希望选择键和selectedKey.isConnectable()在 已成功建立远程连接。这永远不会发生

为了说明这个问题,我在main()方法中编写了一个相当简单的测试。它从两个实例开始 PwpConnectionManager,一个用作服务器,另一个用作客户端。客户端尝试连接到服务器,然后重试 已在服务器端成功建立连接(OP_ACCEPT触发)。但是,我希望OP_连接 在客户端被触发,但从未触发

我已经读到,使用localhost作为连接地址会导致即时连接成功,从而导致 channel.connect()返回true,因此以后不会触发OP_connect。但是,正如您在 在下面的日志输出中,processPendingConnections()中的isConnected()返回false,因此我希望OP_CONNECT返回 最终由选择器选择

以下是运行main()后的输出日志(我已经运行了多次,结果相同):

以下是我的问题:

  • 为什么在客户端中从未触发OP_CONNECT
  • 在检查是否存在错误时,是否以unmanage()中的方式关闭PwpConnectionManager manage()中的serverExecutor.isShutdown()
  • 取消管理()连接管理器时是否需要关闭接受的SocketChannel
  • 还有其他建议和提示吗
  • 守则:

    /**
     * 
     * A connection manager for PWP-connections. It listens for incoming connections and also
     * allows for adding new connections to remote peers.
     * 
     * @author veroslav
     *
     */
    public class PwpConnectionManager {
    
        public static final String DEFAULT_NETWORK_INTERFACE = "";
    
        private static final int SO_RCVBUF_VALUE = 4 * 1024;
        private static final boolean SO_REUSEADDR = true;
    
        private final List<PwpPeer> pendingPeerConnections;
        private final ExecutorService serverExecutor;
    
        private final Selector connectionSelector;
    
        private final String name;
        private final int maxConnections;
        private final int listenPort;
    
        /**
         * 
         * Configure a new connection manager listening on a specified port and serving maxConnections connections
         * 
         * @param name Name identifier for this connection manager (for debugging purposes)
         * @param listenPort Port to listen on for incoming connections
         * @param maxConnections Max simultaneous connections handled by this connection manager
         * @throws IOException If a connection selector can't be opened
         */
        public PwpConnectionManager(final String name, final int listenPort, final int maxConnections) throws IOException {
            this.name = name;
            this.maxConnections = maxConnections;
            this.listenPort = listenPort;
    
            pendingPeerConnections = Collections.synchronizedList(new ArrayList<PwpPeer>()); 
            serverExecutor = Executors.newSingleThreadExecutor();
            connectionSelector = Selector.open();
        }
    
        /**
         * 
         * Start managing the connections (both incoming and outgoing)
         * 
         * @param listenInterface Network interface used to listen for incoming connections
         */
        public void manage(final String listenInterface) {
            serverExecutor.execute(() -> {
                try(final ServerSocketChannel serverChannel = ServerSocketChannel.open()) {
                    if(!serverChannel.isOpen()) {
                        final String errorMessage = name + ": Failed to start server on port " + 
                                listenPort + " with interface " + listenInterface;
                        throw new IOException(errorMessage);
                    }
                    setChannelOptions(serverChannel);
    
                    serverChannel.bind(getSocketAddressFromNetworkInterface(listenInterface), maxConnections);
                    serverChannel.configureBlocking(false);
    
                    serverChannel.register(connectionSelector, SelectionKey.OP_ACCEPT);
    
                    while(true) {                                       
                        if(serverExecutor.isShutdown()) {
                            //TODO: Release and shutdown connection channels, release resources
                            System.out.println(name + ": A shutdown request was issued, trying to shutdown...");
                            break;
                        }
                        System.out.println(name + ": Waiting for selection events...");
                        final int keysSelected = connectionSelector.select();
                        if(keysSelected > 0) {
                            final Set<SelectionKey> selectedKeys = connectionSelector.selectedKeys();
                            final Iterator<SelectionKey> selectedKeysIterator = selectedKeys.iterator();
    
                            while(selectedKeysIterator.hasNext()) {
                                final SelectionKey selectedKey = selectedKeysIterator.next();
                                selectedKeysIterator.remove();
    
                                if(selectedKey.isValid()) {
                                    handleKeySelection(selectedKey);
                                }                           
                            }
                        }
                        //Check for new connection requests to remote peers
                        processPendingConnections();
                    }
                }
                catch(final IOException ioe) {
                    System.err.println(name + ": An error occured while running the server: " + ioe.getMessage());
                }
                System.out.println(name + ": Connection manager was shutdown!");
            }); 
        }
    
        /**
         * 
         * Initialize the shutdown of selector thread and allow it to die
         */
        public void unmanage() {        
            serverExecutor.shutdown();
            connectionSelector.wakeup();        
        }
    
        /**
         * 
         * Add a new peer and try making a connection to it
         * 
         * @param peer The peer we are attempting to connect to
         */
        public void connectTo(final PwpPeer peer) {     
            synchronized (pendingPeerConnections) {
                pendingPeerConnections.add(peer);           
            }       
            connectionSelector.wakeup();
        }
    
        private void processPendingConnections() {
            while(true) {
                PwpPeer peer = null;
                synchronized(pendingPeerConnections) {                           
                    if(!pendingPeerConnections.isEmpty()) {
                        peer = pendingPeerConnections.remove(0);
                    }
                }   
                if(peer == null) {
                    break;
                }
                //TODO: Offload connection attempt to a worker thread? 
                try (final SocketChannel peerConnection = SocketChannel.open()) {
                    peerConnection.configureBlocking(false);
                    setChannelOptions(peerConnection);
    
                    peerConnection.register(connectionSelector, SelectionKey.OP_CONNECT, peer);
                    final boolean isConnected = peerConnection.connect(new InetSocketAddress(peer.getPeerIp(), peer.getPeerPort()));
                    System.out.println(name + ": Connected to remote peer? " + isConnected);
                }
                catch(final IOException ioe) {
                    System.err.println(name + ": Failed to connect to peer: " + ioe.getMessage());
                }
            }
        }
    
        private void handleKeySelection(final SelectionKey selectedKey) throws IOException {
            if(selectedKey.isAcceptable()) {
                //Handle a new connection request
                final ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectedKey.channel();
                try (final SocketChannel connection = serverSocketChannel.accept()){
                    connection.configureBlocking(false);
    
                    final InetSocketAddress connectionAddress = (InetSocketAddress)connection.getRemoteAddress();
                    final String remotePeerIp = connectionAddress.getAddress().getHostAddress();
                    final int remotePeerPort = connectionAddress.getPort();
    
                    final PwpPeer pwpConnection = new PwpPeer(remotePeerIp, remotePeerPort);
    
                    connection.register(selectedKey.selector(), SelectionKey.OP_READ, pwpConnection);
                    System.out.println(name + ": Accepted remote connection");
                } catch (final IOException ioe) {
                    System.err.println(name + ": Failed to accept incoming connection: " + ioe.getMessage());
                }
            }
            else if(selectedKey.isReadable()) {
                //Handle a read attempt to the channel
            }
            else if(selectedKey.isWritable()) {
                //Handle a write attempt to the channel
            }
            else if(selectedKey.isConnectable()) {
                //Handle remote peer accepting our connection attempt
                final SocketChannel peerConnection = ((SocketChannel)selectedKey.channel());
                try {
                    if(peerConnection.finishConnect()) {
                        selectedKey.interestOps(0);
                        System.out.println(name + ": Successfully connected to the remote peer");
                    }
                } catch (final IOException ioe) {
                    // Broken connection, disconnect the peer
                    System.err.println(name + ": Broken connection attempt, disconnecting");
                    peerConnection.close();
                }           
            }
        }
    
        private void setChannelOptions(final NetworkChannel channel) throws IOException {
            channel.setOption(StandardSocketOptions.SO_RCVBUF, PwpConnectionManager.SO_RCVBUF_VALUE);
            channel.setOption(StandardSocketOptions.SO_REUSEADDR, PwpConnectionManager.SO_REUSEADDR);
        }
    
        private InetSocketAddress getSocketAddressFromNetworkInterface(final String networkInterface) {
            try {
                final NetworkInterface listenInterface = NetworkInterface.getByName(networkInterface);
                if(listenInterface == null) {
                    //Invalid/non-existing network interface specified, use default
                    return new InetSocketAddress(listenPort);
                }
                final InetAddress inetAddress = listenInterface.getInetAddresses().nextElement();
                return new InetSocketAddress(inetAddress, listenPort);
            } catch (final SocketException se) {
                //Invalid/non-existing network interface specified, use default
                return new InetSocketAddress(listenPort);
            }       
        }
    
        public static void main(String[] args) throws IOException {
            final int maxConnections = 50;
            final int listenPort = 8006;
    
            final PwpConnectionManager connectionManager = new PwpConnectionManager("SERVER", listenPort, maxConnections);
            connectionManager.manage(PwpConnectionManager.DEFAULT_NETWORK_INTERFACE);
    
            final PwpConnectionManager client = new PwpConnectionManager("CLIENT", listenPort + 1, maxConnections);
            client.manage(PwpConnectionManager.DEFAULT_NETWORK_INTERFACE);
    
            try {
                Thread.sleep(3000);
                client.connectTo(new PwpPeer("localhost", listenPort));
                Thread.sleep(4000);
            } catch (final InterruptedException ie) {
                Thread.interrupted();
            }
            connectionManager.unmanage();
            client.unmanage();
            System.out.println("Main thread done!");
        }
    
    }
    
    /**
    * 
    *用于PWP连接的连接管理器。它侦听传入的连接,并且
    *允许向远程对等方添加新连接。
    * 
    *@作者维罗斯拉夫
    *
    */
    公共类PWP连接管理器{
    公共静态最终字符串DEFAULT_NETWORK_INTERFACE=“”;
    私有静态最终int SO_RCVBUF_值=4*1024;
    私有静态最终布尔值SO_REUSEADDR=true;
    私有最终列表挂起对等连接;
    专用最终执行者服务服务器执行者;
    私人最终选择器连接选择器;
    私有最终字符串名;
    专用最终int maxConnections;
    私人最终int listenPort;
    /**
    * 
    *配置一个新的连接管理器,侦听指定端口并为maxConnections服务
    * 
    *@param name此连接管理器的标识符(用于调试)
    *@param listenPort端口用于侦听传入连接
    *@param maxConnections此连接管理器处理的最大同时连接数
    *@在无法打开连接选择器时引发IOException
    */
    公共PwpConnectionManager(最终字符串名、最终int-listenPort、最终int-maxConnections)引发IOException{
    this.name=名称;
    this.maxConnections=maxConnections;
    this.listenPort=listenPort;
    pendingPeerConnections=Collections.synchronizedList(新的ArrayList());
    serverExecutor=Executors.newSingleThreadExecutor();
    connectionSelector=Selector.open();
    }
    /**
    * 
    *开始管理连接(传入和传出)
    * 
    *@param listenterface用于侦听传入连接的网络接口
    */
    公共void管理(最终字符串列表接口){
    serverExecutor.execute(()->{
    尝试(最终服务器socketchannel serverChannel=ServerSocketChannel.open()){
    如果(!serverChannel.isOpen()){
    最终字符串errorMessage=name+“:无法在端口“+上启动服务器
    listenPort+“带接口”+listenterface;
    抛出新IOException(errorMessage);
    }
    设置频道选项(服务器频道);
    bind(getSocketAddressFromNetworkInterface(Listenterface),maxConnections);
    serverChannel.configureBlocking(false);
    serverChannel.register(connectionSelector,SelectionKey.OP_ACCEPT);
    虽然(正确){
    if(serverExecutor.isShutdown()){
    //TODO:释放和关闭连接通道,释放资源
    System.out.println(name+“:发出关闭请求,试图关闭…”);
    打破
    }
    System.out.println(name+“:等待选择事件…”);
    final int keyselected=connectionSelector.select();
    如果(选择的键>0){
    最终设置selectedKeys=connectionSelector.selectedKeys();
    最终迭代器SelectedKey迭代器=selectedKeys.Iterator();
    while(selectedKey迭代器.hasNext()){
    final SelectionKey selectedKey=selectedKeysIterator.next();
    selectedKeysIterator.remove();
    if(selectedKey.isValid()){
    HandleKey选择(selectedKey);
    }                           
    }
    }
    //检查到远程对等方的新连接请求
    processPendingConnections();
    }
    }
    捕获(最终ioe异常ioe){
    System.err.println(name+):运行服务器时出错:“+ioe.getMessage()
    
    /**
     * 
     * A connection manager for PWP-connections. It listens for incoming connections and also
     * allows for adding new connections to remote peers.
     * 
     * @author veroslav
     *
     */
    public class PwpConnectionManager {
    
        public static final String DEFAULT_NETWORK_INTERFACE = "";
    
        private static final int SO_RCVBUF_VALUE = 4 * 1024;
        private static final boolean SO_REUSEADDR = true;
    
        private final List<PwpPeer> pendingPeerConnections;
        private final ExecutorService serverExecutor;
    
        private final Selector connectionSelector;
    
        private final String name;
        private final int maxConnections;
        private final int listenPort;
    
        /**
         * 
         * Configure a new connection manager listening on a specified port and serving maxConnections connections
         * 
         * @param name Name identifier for this connection manager (for debugging purposes)
         * @param listenPort Port to listen on for incoming connections
         * @param maxConnections Max simultaneous connections handled by this connection manager
         * @throws IOException If a connection selector can't be opened
         */
        public PwpConnectionManager(final String name, final int listenPort, final int maxConnections) throws IOException {
            this.name = name;
            this.maxConnections = maxConnections;
            this.listenPort = listenPort;
    
            pendingPeerConnections = Collections.synchronizedList(new ArrayList<PwpPeer>()); 
            serverExecutor = Executors.newSingleThreadExecutor();
            connectionSelector = Selector.open();
        }
    
        /**
         * 
         * Start managing the connections (both incoming and outgoing)
         * 
         * @param listenInterface Network interface used to listen for incoming connections
         */
        public void manage(final String listenInterface) {
            serverExecutor.execute(() -> {
                try(final ServerSocketChannel serverChannel = ServerSocketChannel.open()) {
                    if(!serverChannel.isOpen()) {
                        final String errorMessage = name + ": Failed to start server on port " + 
                                listenPort + " with interface " + listenInterface;
                        throw new IOException(errorMessage);
                    }
                    setChannelOptions(serverChannel);
    
                    serverChannel.bind(getSocketAddressFromNetworkInterface(listenInterface), maxConnections);
                    serverChannel.configureBlocking(false);
    
                    serverChannel.register(connectionSelector, SelectionKey.OP_ACCEPT);
    
                    while(true) {                                       
                        if(serverExecutor.isShutdown()) {
                            //TODO: Release and shutdown connection channels, release resources
                            System.out.println(name + ": A shutdown request was issued, trying to shutdown...");
                            break;
                        }
                        System.out.println(name + ": Waiting for selection events...");
                        final int keysSelected = connectionSelector.select();
                        if(keysSelected > 0) {
                            final Set<SelectionKey> selectedKeys = connectionSelector.selectedKeys();
                            final Iterator<SelectionKey> selectedKeysIterator = selectedKeys.iterator();
    
                            while(selectedKeysIterator.hasNext()) {
                                final SelectionKey selectedKey = selectedKeysIterator.next();
                                selectedKeysIterator.remove();
    
                                if(selectedKey.isValid()) {
                                    handleKeySelection(selectedKey);
                                }                           
                            }
                        }
                        //Check for new connection requests to remote peers
                        processPendingConnections();
                    }
                }
                catch(final IOException ioe) {
                    System.err.println(name + ": An error occured while running the server: " + ioe.getMessage());
                }
                System.out.println(name + ": Connection manager was shutdown!");
            }); 
        }
    
        /**
         * 
         * Initialize the shutdown of selector thread and allow it to die
         */
        public void unmanage() {        
            serverExecutor.shutdown();
            connectionSelector.wakeup();        
        }
    
        /**
         * 
         * Add a new peer and try making a connection to it
         * 
         * @param peer The peer we are attempting to connect to
         */
        public void connectTo(final PwpPeer peer) {     
            synchronized (pendingPeerConnections) {
                pendingPeerConnections.add(peer);           
            }       
            connectionSelector.wakeup();
        }
    
        private void processPendingConnections() {
            while(true) {
                PwpPeer peer = null;
                synchronized(pendingPeerConnections) {                           
                    if(!pendingPeerConnections.isEmpty()) {
                        peer = pendingPeerConnections.remove(0);
                    }
                }   
                if(peer == null) {
                    break;
                }
                //TODO: Offload connection attempt to a worker thread? 
                try (final SocketChannel peerConnection = SocketChannel.open()) {
                    peerConnection.configureBlocking(false);
                    setChannelOptions(peerConnection);
    
                    peerConnection.register(connectionSelector, SelectionKey.OP_CONNECT, peer);
                    final boolean isConnected = peerConnection.connect(new InetSocketAddress(peer.getPeerIp(), peer.getPeerPort()));
                    System.out.println(name + ": Connected to remote peer? " + isConnected);
                }
                catch(final IOException ioe) {
                    System.err.println(name + ": Failed to connect to peer: " + ioe.getMessage());
                }
            }
        }
    
        private void handleKeySelection(final SelectionKey selectedKey) throws IOException {
            if(selectedKey.isAcceptable()) {
                //Handle a new connection request
                final ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectedKey.channel();
                try (final SocketChannel connection = serverSocketChannel.accept()){
                    connection.configureBlocking(false);
    
                    final InetSocketAddress connectionAddress = (InetSocketAddress)connection.getRemoteAddress();
                    final String remotePeerIp = connectionAddress.getAddress().getHostAddress();
                    final int remotePeerPort = connectionAddress.getPort();
    
                    final PwpPeer pwpConnection = new PwpPeer(remotePeerIp, remotePeerPort);
    
                    connection.register(selectedKey.selector(), SelectionKey.OP_READ, pwpConnection);
                    System.out.println(name + ": Accepted remote connection");
                } catch (final IOException ioe) {
                    System.err.println(name + ": Failed to accept incoming connection: " + ioe.getMessage());
                }
            }
            else if(selectedKey.isReadable()) {
                //Handle a read attempt to the channel
            }
            else if(selectedKey.isWritable()) {
                //Handle a write attempt to the channel
            }
            else if(selectedKey.isConnectable()) {
                //Handle remote peer accepting our connection attempt
                final SocketChannel peerConnection = ((SocketChannel)selectedKey.channel());
                try {
                    if(peerConnection.finishConnect()) {
                        selectedKey.interestOps(0);
                        System.out.println(name + ": Successfully connected to the remote peer");
                    }
                } catch (final IOException ioe) {
                    // Broken connection, disconnect the peer
                    System.err.println(name + ": Broken connection attempt, disconnecting");
                    peerConnection.close();
                }           
            }
        }
    
        private void setChannelOptions(final NetworkChannel channel) throws IOException {
            channel.setOption(StandardSocketOptions.SO_RCVBUF, PwpConnectionManager.SO_RCVBUF_VALUE);
            channel.setOption(StandardSocketOptions.SO_REUSEADDR, PwpConnectionManager.SO_REUSEADDR);
        }
    
        private InetSocketAddress getSocketAddressFromNetworkInterface(final String networkInterface) {
            try {
                final NetworkInterface listenInterface = NetworkInterface.getByName(networkInterface);
                if(listenInterface == null) {
                    //Invalid/non-existing network interface specified, use default
                    return new InetSocketAddress(listenPort);
                }
                final InetAddress inetAddress = listenInterface.getInetAddresses().nextElement();
                return new InetSocketAddress(inetAddress, listenPort);
            } catch (final SocketException se) {
                //Invalid/non-existing network interface specified, use default
                return new InetSocketAddress(listenPort);
            }       
        }
    
        public static void main(String[] args) throws IOException {
            final int maxConnections = 50;
            final int listenPort = 8006;
    
            final PwpConnectionManager connectionManager = new PwpConnectionManager("SERVER", listenPort, maxConnections);
            connectionManager.manage(PwpConnectionManager.DEFAULT_NETWORK_INTERFACE);
    
            final PwpConnectionManager client = new PwpConnectionManager("CLIENT", listenPort + 1, maxConnections);
            client.manage(PwpConnectionManager.DEFAULT_NETWORK_INTERFACE);
    
            try {
                Thread.sleep(3000);
                client.connectTo(new PwpPeer("localhost", listenPort));
                Thread.sleep(4000);
            } catch (final InterruptedException ie) {
                Thread.interrupted();
            }
            connectionManager.unmanage();
            client.unmanage();
            System.out.println("Main thread done!");
        }
    
    }
    
    if(selectedKey.isAcceptable()) {
                //Handle a new connection request
                System.out.println(name + ": Got an OP_ACCEPT key");
                final ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectedKey.channel();
                SocketChannel connection = null;
                try {
                    connection = serverSocketChannel.accept();
                    connection.configureBlocking(false);
    
                    final InetSocketAddress connectionAddress = (InetSocketAddress)connection.getRemoteAddress();
                    final String remotePeerIp = connectionAddress.getAddress().getHostAddress();
                    final int remotePeerPort = connectionAddress.getPort();
    
                    final PwpPeer pwpConnection = new PwpPeer(remotePeerIp, remotePeerPort);
    
                    connection.register(selectedKey.selector(), SelectionKey.OP_READ, pwpConnection);
                    System.out.println(name + ": Accepted remote connection: ip: " + remotePeerIp + ", port " + remotePeerPort);
                } catch (final IOException ioe) {
                    System.err.println(name + ": Failed to accept incoming connection: " + ioe.getMessage());
                    if(connection != null) {
                        connection.close();
                    }
                }
            }