Java nio服务器总是停留在selector.select上
我正在尝试使用NIO编写简单的客户机服务器模型(以了解它如何工作)。 有两种类型的消息可以发送Connection(这是服务器发送给客户端的消息,当它连接给它的信息时,如客户端ID)和Ping。
如果客户端没有被ping超过4秒的时间段,服务器将ping客户端,客户端将响应乒乓。
当我初始化我的服务器时,我这样做:
try{
selector = Selector.open();
selector.wakeup();
serverChannel = selector.provider().openServerSocketChannel();
serverChannel.socket().bind(new InetSocketAddress(port));
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}catch (IOException e){
log.Error(e);
throw new RuntimeException("Error opening selector.", e);
}
在发生这种情况之后,我们进入我们的服务器循环,它将查找哪些键已被选择并在键上执行所选操作
int select = 0;
while (running){
try{
select = selector.select(timeout);
if(select == 0){
continue;
}
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while(selectedKeys.hasNext()){
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
Connection conn = (Connection)key.attachment();
if(conn != null){
if(key.isValid() && key.isReadable()){
//handle key reads
}
if(key.isValid() && key.isWritable()){
//handle key writes
}
continue;
}
if(key.isValid() && key.isAcceptable()){
Connection newConn = new Connection(transport, packetFactory, nextConnectionId);
try{
SelectionKey selectionKey = newConn.getConnection().handleAccept(selector, serverChannel.accept());
selectionKey.attach(newConn);
//send the client a connection message
nextConnectionId++;
}catch(IOException e){
newConn.close();
log.Error(e);
}
}
}
long time = System.nanoTime();
for(Connection c : connections){
if(c.getConnection().isTimedOut(time)){
c.close();
}
if(c.needsPing(time)) {
c.sendPing();
}
}
}catch(IOException e){
log.Error(e);
}
}
handleAccept()方法看起来像这样
public SelectionKey handleAccept(Selector selector, SocketChannel ch) {
try{
socketChannel = ch;
socketChannel.configureBlocking(false);
lastReadTime = System.nanoTime();
selectionKey = ch.register(selector, SelectionKey.OP_READ);
return selectionKey;
}catch(IOException e){
close();
}
}
handleAccept()方法是连接对象的一部分,它保存诸如连接socketChannel,selectionkey之类的信息。 每当连接被读取/写入时,这些更新如下所示:
public void handleWrite(SelectionKey key) throws IOException {
socketChannel = (SocketChannel) key.channel();
this.selectionKey = key;
//do other stuff
}
最后,当我们需要向客户端写入内容时,我们告诉socketChannel我们有兴趣编写代码,一旦我们写完了,我们会告诉它我们想要阅读。 这是通过访问连接选择键并将其interestOps()
设置为正确的字段来完成的。
当我运行这个代码时,服务器执行以下操作:
[2014/05/25 01:49:23] [INFO] [com.adammflax.net.ServerEndPoint] Started Server on port 9001
[2014/05/25 01:49:26] [INFO] [com.adammflax.net.ServerEndPoint] Added the connection com.adammflax.net.Connection@4838ddcc to the list of connections on the server
[2014/05/25 01:49:26] [INFO] [com.adammflax.net.ServerEndPoint] Client com.adammflax.net.Connection@4838ddcc connected to the server given a Id of 0
30
[CLIENT RECEIVES CONNECTION PACKET]
[2014/05/25 01:49:26] [DEBUG] [com.adammflax.net.ServerEndPoint] Received ping from client now replying
[CLIENT RECEIVES Ping PACKET]
在这之后,服务器只是停留在selector.select()(它总会超时并返回0)。 任何人都可以向我解释为什么发生这种情况。 我知道这是很多需要深入研究的代码(并且试图压缩这些代码的缺失),但是对此问题的任何帮助都会令人惊叹。
编辑添加更多代码以显示我们如何检测ping是否需要发生(属于连接对象)
public boolean needsPing(long time) {
if(!isConnected()){
return false;
}
if(pingTime <= 0){
return false;
}
if(time <= lastPingSentTime + pingTime){
return false;
}
lastPingSentTime = System.nanoTime();
return true;
}
发送ping将一个pingPacket对象转换为字节并调用send方法,该方法将字节添加到名为messages的messageQueue。
public void send(byte[] message) {
messages.add(message);
selectionKey.interestOps(SelectionKey.OP_WRITE);
}
因为我们现在在OP_WRITE中handleWrite
最终会被调用
public void handleWrite(SelectionKey key) throws IOException {
socketChannel = (SocketChannel) key.channel();
if(socketChannel == null){
key.cancel();
throw new SocketException("Socket connection has closed!");
}
while(!messages.isEmpty()){
try{
writeBuffer = ByteBuffer.wrap(messages.peek());
if(socketChannel.write(writeBuffer) == 0){
break;
}
if (writeBuffer.remaining() > 0) {
break;
}
writeBuffer.compact();
messages.poll(); //everything went well so remove head
}catch(IOException e){
log.Error("failed to write : " + messages.peek() + " at " +
this.socketChannel.getRemoteAddress());
}
}
selectionKey.interestOps(SelectionKey.OP_READ);
}
链接地址: http://www.djcxy.com/p/34129.html
上一篇: Java nio server always stuck on selector.select
下一篇: Java nio, close socketChannel but leave ServerSocketChannel opened