论坛首页 Java企业应用论坛

java nio channel调用register方法出现阻塞

浏览 15041 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (16) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-09-20   最后修改:2010-09-20
刚开始学习nio,写了一个基于nio的http client,代码如下(初学写的仓促欢迎指出错误):
public class NIODemo {

    private final Selector selector;

    private CharsetEncoder encoder = Charset.forName("utf-8").newEncoder();

    protected CharsetDecoder decoder;

    public NIODemo() throws IOException {

        Charset charset = Charset.forName("utf-8");

        decoder = charset.newDecoder();

        selector = Selector.open();
    }

    public void fetch() throws IOException {

        SocketChannel client = SocketChannel.open();

        client.configureBlocking(false);

        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 80);

        client.register(selector, SelectionKey.OP_CONNECT);

        client.connect(address);
    }

    public void start() throws IOException {

        Executors.newSingleThreadExecutor().execute(new Runnable() {

            @Override
            public void run() {

                try {
                    while (true) {

                        System.out.println("before select.");

                        selector.select();

                        System.out.println("after select.");

                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();

                        while (iterator.hasNext()) {

                            SelectionKey key = iterator.next();

                            iterator.remove();

                            handleKey(key);

                        }

                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        });

    }

    private void handleKey(SelectionKey key) throws IOException {

        SocketChannel channel = (SocketChannel) key.channel();

        if (key.isConnectable()) {

            System.out.println("key is connnectable");

            if (channel.isConnectionPending()) {
                channel.finishConnect();
            }

            channel.register(selector, SelectionKey.OP_WRITE);

        } else if (key.isWritable()) {

            ByteBuffer block = encoder.encode(CharBuffer.wrap("GET / HTTP/1.1\r\nHost: www.iteye.com\r\n\r\n"));

            channel.write(block);

            channel.register(selector, SelectionKey.OP_READ);

        } else if (key.isReadable()) {

            ByteBuffer clientBuffer = ByteBuffer.allocate(1024 * 10);

            int count = channel.read(clientBuffer);

            if (count > 0) {
                clientBuffer.flip();

                CharBuffer charBuffer = decoder.decode(clientBuffer);

                System.out.println(charBuffer.toString());
            } else {
                channel.close();
            }

        }

    }

    public static void main(String[] args) throws Exception {

        NIODemo client = new NIODemo();

        client.start();

        client.fetch();

    }

发现运行时总是阻塞在fetch方法中channel往selector注册处,
client.register(selector, SelectionKey.OP_CONNECT);


如果main方法中先调用fetch()再调用start()则不会出现此问题
public static void main(String[] args) throws Exception {

        NIODemo client = new NIODemo();

        client.fetch();
        
        client.start();

    }


请问为什么出现这种现象,多谢
   发表时间:2010-09-20   最后修改:2010-09-21
仔细看了下api找到了答案,对于channel的register方法有如下说明:
引用
This method will then synchronize on the selector's key set and therefore may block if invoked concurrently with another registration or selection operation involving the same selector.


当第一次调用start方法时,selector阻塞在select方法上,此时再调用fetch(),根据上文api所说,此时channel的register(..)将阻塞直到selector.select()返回.
可以通过调用selector.wakeup()或者对阻塞在selector.select()处的thread调用interrupt方法使select()返回.
0 请登录后投票
   发表时间:2010-09-21  
路过,学习一下,这边基本都用mina,没深入了解过NIO
0 请登录后投票
   发表时间:2010-09-21  
你connect事件都没注册怎么可能连接上。
0 请登录后投票
   发表时间:2010-09-21  
我觉得,你这样写不属于nio client。nio的http client应该是一个客户端连续多发起http请求。然后根据之前注册的key,分别处理返回的请求数据。
0 请登录后投票
   发表时间:2010-09-21   最后修改:2010-09-21
xuganggogo 写道
我觉得,你这样写不属于nio client。nio的http client应该是一个客户端连续多发起http请求。然后根据之前注册的key,分别处理返回的请求数据。

其实我要做得client类似一个爬虫,不间断的往里面放入url,然后这个client负责把网页内容抓回来
NIODemo client = new NIODemo();
client.start();
client.fetch("http://xxx1");
client.fetch("http://xxx2");
client.fetch("http://xxx2");
0 请登录后投票
   发表时间:2010-09-21   最后修改:2010-09-21
在client.register(selector, SelectionKey.OP_CONNECT);前调用
selector.wakeup();
selector.select();阻塞了selector所在的线程。由于当前的keyset是空的没有事件唤醒它。
当register时需要同步selector中的key set
这么做也不能百分之百保证,如果selector.wakeup();后当前进程挂起,有切换到了
selector.select()还会被挂起,不过机率是相当的低。最好select()加一个超时时间
select(timeout)
--------------------------------------------------------------
# public void fetch() throws IOException { 
#  
#         SocketChannel client = SocketChannel.open(); 
#  
#         client.configureBlocking(false); 
#  
#         InetSocketAddress address = new InetSocketAddress("127.0.0.1", 80); 
#         selector.wakeup();
#         client.register(selector, SelectionKey.OP_CONNECT); 
#  
#         client.connect(address); 
#     } 
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics