Java NIO:OP_CONNECT从未在客户机中触发,即使连接已被服务器接受
我正在学习JavaNIO,并尝试使用选择器编写一个简单的客户机/服务器应用程序。到目前为止,我已经 一个PwpConnectionManager类,既充当侦听传入连接的服务器,也充当客户端 与远程对等方建立连接。要连接到的远程对等方是通过connectTo()方法添加的 虽然检测和接受传入连接似乎有效,但我无法在以下情况下触发OP_CONNECT事件: 正在尝试连接到远程对等点。我希望选择键和selectedKey.isConnectable()在 已成功建立远程连接。这永远不会发生 为了说明这个问题,我在main()方法中编写了一个相当简单的测试。它从两个实例开始 PwpConnectionManager,一个用作服务器,另一个用作客户端。客户端尝试连接到服务器,然后重试 已在服务器端成功建立连接(OP_ACCEPT触发)。但是,我希望OP_连接 在客户端被触发,但从未触发 我已经读到,使用localhost作为连接地址会导致即时连接成功,从而导致 channel.connect()返回true,因此以后不会触发OP_connect。但是,正如您在 在下面的日志输出中,processPendingConnections()中的isConnected()返回false,因此我希望OP_CONNECT返回 最终由选择器选择 以下是运行main()后的输出日志(我已经运行了多次,结果相同): 以下是我的问题:Java NIO:OP_CONNECT从未在客户机中触发,即使连接已被服务器接受,java,networking,nio,Java,Networking,Nio,我正在学习JavaNIO,并尝试使用选择器编写一个简单的客户机/服务器应用程序。到目前为止,我已经 一个PwpConnectionManager类,既充当侦听传入连接的服务器,也充当客户端 与远程对等方建立连接。要连接到的远程对等方是通过connectTo()方法添加的 虽然检测和接受传入连接似乎有效,但我无法在以下情况下触发OP_CONNECT事件: 正在尝试连接到远程对等点。我希望选择键和selectedKey.isConnectable()在 已成功建立远程连接。这永远不会发生 为了说明这
/**
*
* 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();
}
}
}