我创建了一个异步客户端套接字,该套接字连接到服务器,并始终保持连接打开,并在与服务器断开连接时自动重新连接。
无论如何,我的问题是:初始化连接时,我将该操作注册为连接操作(SelectionKey.OP_Connect
)。
现在,当我在选择器的选定键之间进行迭代时,我希望它进入可连接的控制语句,并按照控制语句中的说明更改操作。
不幸的是,我没有任何选定的键。我哪里出问题了?
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
* @author kewin
*/
public class clientEvent implements Runnable{
private static volatile boolean connected=false;
private SocketChannel channel;
private Selector selector;
private ByteBuffer readBuffer = ByteBuffer.allocate(8192);
private QueueData Qdata;
private SelectionKey selectKey;
private void initiateConnection() {
try {
selector= Selector.open();//initializes the Selector Thread
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress(InetAddress.getByName("127.0.0.1"),2000));
Thread.sleep(50);//Sleeps for a few Seconds to allow decent connection completion
channel.register(selector,SelectionKey.OP_CONNECT);//Registers the channel with the a selector and a selection key
if(channel.finishConnect())/*checks whether the connection is finished*/{
channel.register(selector,SelectionKey.OP_READ);//Registers the channel with the a selector and a selection key
connected=true;}
} catch(Exception ex){connected=false;}
}
private void ConnectAndMonitor(){
while(true){
try {
readBuffer.clear();
channel.read(readBuffer);
} catch (Exception ex) {
connected=false
try {
channel.close()
selector.close();
} catch (Exception e) {
}
if(!connected){
initiateConnection();
connected=true;
}
}
}
}
@Override
public void run() {
try{
new Thread(){@Override public void run(){ConnectAndMonitor()}}.start();
while(true){
if(!connected){
System.out.println("not connected");
Thread.sleep(500);
}
else{
selector.selectNow();
Set Keys=selector.keys();
Iterator iterator =Keys.iterator();
while(iterator.hasNext()){
SelectionKey key=(SelectionKey)iterator.next();
if(key.isConnectable()){
channel.register(selector,SelectionKey.OP_READ);
System.out.println("Connectable");
break;
}
if(key.isReadable()){
channel.register(selector,SelectionKey.OP_WRITE);
System.out.println("Readable");
break;
}
if(key.isWritable()){
channel.register(selector,SelectionKey.OP_CONNECT);
System.out.println("Writable");
break;
}
System.out.println("<<<<<<<<<>>>>>>>>>>>>");
}
Thread.sleep(1000);
}
}
}
catch(Exception ex){
}
}
public static void main(String[] args){
new Thread(new clientEvent()).start();
}
}
最佳答案
如果以阻塞模式进行连接,它将阻塞并在方法调用期间完成,因此,如果您随后将其注册为OP_CONNECT,则isConnectable()
永远不会为真。
因此,如果您必须具有非阻塞连接(代码已在其中执行此操作),请首先将通道置于非阻塞模式,发出connect()
,注册OP_CONNECT,然后在触发时,`finishConnect()返回true ,取消注册OP_CONNECT并注册其他内容,或者如果您是客户端,则很可能只是开始发送请求。
编辑:通过以下方式,您编辑的代码不正确:
删除Thread.sleep()
。
将finishConnect()
调用移到选择循环的isConnected()
情况下。
当OP_CONNECT/isConnectable()
触发时,注销OP_CONNECT。
删除if(!connected)
测试。
我上面已经说过大部分。目前,您是(a)给予完成连接的时间,(b)完成连接,(c)在选择循环中等待其完成,(d)即使您不感兴趣,也不要注销OP_CONNECT
(e)如果没有连接,则从不执行select();(f)尝试在select()循环中查找完整的连接。这根本没有任何意义。
另一方面,如果您不介意在创建连接的地方等待连接完成内联,那么为什么要完全以非阻塞模式进行连接?以阻止模式进行操作,然后configureBlocking(false),
然后注册OP_READ/OP_WRITE
并完全忘记OP_CONNECT
,isConnectable()
和finishConnect()
。