weixin_42406381
2010-12-27 12:18
浏览 476
已采纳

Java NIO read阻塞问题(急需解决!,高手素来!!)

我做的是服务端Socket,各位高手先看看代码吧

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(CommonBean.getSocketPort()));
serverChannel.configureBlocking(false);
Selector acceptSelector = SelectorProvider.provider().openSelector();
SelectionKey acceptKey = serverChannel.register(acceptSelector, SelectionKey.OP_ACCEPT);
int keysAdded = 0;
int k = 0;
while ((keysAdded = acceptSelector.select(1000 * 60)) > 0) {
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
while (i.hasNext()) {
SelectionKey selectionkey = (SelectionKey) i.next();
i.remove();
if (selectionkey != null) {
new Thread(new SocketThread(selectionkey)).start();
}
}
}

public class SocketThread extends Thread {

public SocketThread(SelectionKey selectionkey) {
this.selectionkey = selectionkey;
}

public void run() {

try {
ServerSocketChannel nextReady = (ServerSocketChannel) selectionkey.channel();
socketChannel = nextReady.accept();

if (socketChannel != null){
socketLog.info("[ "+socketChannel.socket().getInetAddress().getHostAddress()+" ] Thread started....");
processRequest();
socketLog.info("[ "+socketChannel.socket().getInetAddress().getHostAddress()+" ] Thread ended...");
}

} catch (Exception e) {
socketLog.error(e.getMessage(), e);
}
}

private void processRequest() {
try {
while (true) {
ByteBuffer tempMsgID = ByteBuffer.allocate(4);
tempMsgID.clear();
socketChannel.read(tempMsgID);
tempMsgID.flip();
if (tempMsgID.limit() == 0) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
socketChannel.socket().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

我把服务端建立socket通讯的主要代码都摘取出来了,麻烦各位高手分析一下问题的原因以及解决的办法,我的问题如下:

一、我在前面已经设置了serverChannel.configureBlocking(false);为非阻塞模式,为什么当我调用socketChannel.read(tempMsgID);的时候,read依然用阻塞的方式去读取数据呢?

二、测试时候发现,如果客户端拔网线或者拔电源时,服务端不知道客户端退出,所以read就永远死在程序里了,当前的这个线程就一直存在了,我现在想设置read的timeout,比如说不管客户端异常退出还是由于其他原因,只要read超过3分钟,我就将当前这个socket关闭退出,在网上找了好久,没找到如何能设置socketChannel的timeout方法,不知道是我没找到,socketChannel就无法设置超时,如果无法设置超时,那么我的这个问题有没有别的解决办法呢?

三、我在网上查找socketChannel的timeout的方法时,发现socket可以设置超时,所以想到将socket从socketChannel中提取出来,用socket的方式进行读取,例如:socketChannel.socket().getinputstream();虽然说新版的NIO和旧版的纯socket相结合使用有点不伦不类,但是数据真的能正常收取,并且设置timeout也按时退出了,但是这种方式有新的问题,当我用20台客户端并发访问服务端的时候,运行一段时间就报too many open files的错误了,我检查程序了,我在最后的finally模块中,确实每次都将inputstream和socket全都close()了,但是依然解决不了too many open files异常,不知道该怎么办了。

希望NIO方面的高手来指点一下,NIO我接触的不多,现在这几个问题实在是没有能力解决了。

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • softtech2008 2010-12-27 16:21
    已采纳
    1. socketchannel也设置成nonblocking,试试
    2. 在serversocket里面设置超时时间
    3. 这可能跟这种混用有关系。 因为selector是跟channel打交道的,channel的状态检查发生在什么时候? 是每次selector访问的时候还是什么时候?如果是前者,那么当socket 关闭的时候channel未必能检测到一端socket的状态改变,而nio服务端检测的是channel的状态,所以socket就这么打开着了。(传统server会能直接检测到socket的异常,而nio检测的是channel,所以直接操作socket会丢失这种异常?)

    呵呵,我也是提供一种思路。

    点赞 打赏 评论
  • iteye_5200 2010-12-27 19:14

    首先你的程序问题还是很多的,建议你看看O'Reilly的java nio这本书。
    还有就是nio陷阱很多,建议还是使用开源的nio库如mina来使用网络通信。下面在说说你提到的几个问题。

    1.serverChannel.configureBlocking(false);是吧监听端口的socket设置成了非阻塞,并没有把accept的socket调用.configureBlocking(false);所以read肯定是阻塞的。

    2.nio的超时是通过记录各个事件如read write的时间点通过轮询来实现的,否则就不是nio非阻塞通信了。

    3.too many open file可能是打开的socket超过单个进程打开文件数造成的,在linux下面默认是1024个。

    点赞 打赏 评论

相关推荐 更多相似问题