开发者社区> 问答> 正文

关于NIO读取数据的问题:报错

		InetSocketAddress ia = new InetSocketAddress("www.baidu.com", 80);
		SocketChannel socket = SocketChannel.open(ia);
		socket.configureBlocking(false);
		byte[] get = "GET / HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n".getBytes();
		ByteBuffer gets = ByteBuffer.allocate(get.length);
		gets.put(get);
		gets.flip();
		socket.write(gets);
		
		ByteBuffer buf = ByteBuffer.allocate(1024);
		int len = -1;
		
		Thread.sleep(100);
		while((len=socket.read(buf)) > 0) {
			buf.flip();
			System.out.println(new String(buf.array(),0,buf.limit(),"GBK"));
			buf.clear();
		}

当设置了非阻塞模式后,如果立马就去读取远程主机的数据,会返回0。如果等待一段时间,比如100毫秒,再去读取,则正常。

不清楚NIO内部怎么实现的,但第一次真正读取返回数据之前,似乎有一些准备工作,或者因为写"GET / HTTP/1.1" 数据包还没有真正发送到服务端,就开始读了,导致服务端还没有数据返回。

有没有什么办法能判断出第一次读的时候所有必备条件都已经准备好了。

或者有什么API能判断?

展开
收起
kun坤 2020-06-07 21:18:04 624 0
1 条回答
写回答
取消 提交回答
  • 问题不错######设置了非阻塞模式就需要用selector来做了,你这样sleep根据网络条件来的,而且如果缓冲区弄小一点的话,你这样sleep也是不成立的,我改了一个用selector的,希望对你有点帮助

    SocketChannel socketChannel = SocketChannel.open(); SocketAddress remote = new InetSocketAddress("www.baidu.com", 80); socketChannel.configureBlocking(false); socketChannel.connect(remote);

    	Selector selector = Selector.open();
    	socketChannel.register(selector, SelectionKey.OP_CONNECT);		// 关注连接事件
    	
    	byte[] get = "GET / HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n".getBytes();
    	ByteBuffer buf = ByteBuffer.wrap(get);
    	while(true) {
    		if(selector.select() == 0) {
    			continue;
    		}
    		for(Iterator<SelectionKey> iter = selector.selectedKeys().iterator();iter.hasNext();) {
    			SelectionKey key = iter.next();
    			if(key.isConnectable()) {
    				if(socketChannel.finishConnect()) {	// 初始化连接获得 clientsocket
    					buf.put(get);
    					buf.flip();
    					socketChannel.register(selector, SelectionKey.OP_WRITE);		// 连接成功关注写事件
    				}
    			}
    			if(key.isWritable()) {
    				if(buf.hasRemaining()) {
    					socketChannel.write(buf);
    				} else {	//写入完毕,开始关注读取
    					socketChannel.register(selector, SelectionKey.OP_READ);		
    				}
    			}
    			if(key.isReadable()) {
    				buf.clear();
    				if(socketChannel.read(buf) >= 0 ) {
    					buf.flip();
    					System.out.print(new String(buf.array(),0,buf.limit(),"GBK"));
    				}
    			}
    			iter.remove();
    		}
    	}</pre> 
    

    ######多谢楼上的兄弟,不过我发现还是有个问题,原因出在这里
    if(selector.select() == 0) {
        continue;
    }

    看了文档,selector.select() 使用的是阻塞的方式,最终是当所有的数据接收到后,又一次执行了

    selector.select(),结果就卡在那里了,等了一段时间超时就自己退出了。

    如果将if(key.isReadable()) 改为这样:

    if(key.isReadable()) {
        buf.clear();
        while(socket.read(buf) > 0) {
            buf.flip();
            System.out.print(new String(buf.array(), 0, buf.limit(),"GBK"));
            buf.clear();
        }
        break done;
    }

    这样也不行,虽然瞬间就退出了,也没阻塞,但是读取的数据不完整,可能有时候就是会读取到0个字节的数据,但是此时的返回的数据流还没有真正的结束。看来在if(key.isReadable())里面写个循环读取也不靠谱。另外写成 selector.selectNow()也不行。

    ######建议你再去看看nio方面的东西,非阻塞模式的精髓就是那个select######

    看了一下,selector作为多线程之间的交互,接受线程当接受到请求之后,将socket注册到selector,注册为已经接受连接,然后继续等待接受。

    select线程就处理是请求已经完成了连接建立,读,写准备好的检查,如果都已经完成,则交给工作线程去完成。大体上是分这么三步骤的。

    不过如果是客户端的也这么做的话, 当完成了读写逻辑之后,接受线程会再次等待接受,这样程序还是会卡在那里,最多只能加一个超时时间然后退出。

     

    ######楼主的例子 貌似跑不起来啊
    2020-06-07 21:18:09
    赞同 展开评论 打赏
问答分类:
问答标签:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载