NIO Server无法收听客户端

时间:2009-05-25 11:44:35

标签: java tcp nio

您好我正在尝试实现一个简单的Java NIO服务器;它将socketChannel注册到选择器。因此,我希望听取客户的意见并发回一些回复。使用选择器注册socketChannel后,即使客户端(非NIO)发送了一些数据,服务器也无法读取; howerver生成的密钥仍在迭代中。

详细视图:服务器端:

**First thread**:

public void run(){ 而(真){

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(true);
    serverSocketChannel.socket().bind(inetAdressOfServer);
    SocketChannel clientChannel = serverSocketChannel.accept();
    new Listener("").addSocketChannel(clientChannel);

}}

**Second Thread**:

    static Selector selector = Selector.open();
    public boolean addSocketChannel(SocketChannel clientChannel) {

        SelectionKey key = clientSocketChannel.register(selector, selector.OP_READ|SelectionKey.OP_WRITE);              
        key.attach(new ChannelCallback(clientSocketChannel));
        return key.isValid();
    }

    public void run() {

        Set keysSet = selector.keys();
        Iterator i = keysSet.iterator();        
        while (i.hasNext()) {
            SelectionKey key = (SelectionKey) i.next();
        }

        if (key.isReadable()) {
            //read and do something
        }
    }



Client Side:

Socket socket = new Socket(serverIP, serverPort);    
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());    
dos.writeBytes(str + "\n");

注意:当在单线程中完成时,相同的程序可以工作,但是当以上述方式实现时,会导致它不能监听客户端。 请帮我解决这个问题。

2 个答案:

答案 0 :(得分:1)

很难看到你在那里做了什么,但看起来两个线程都使用了你标记为“第二线程”的内容(对实现Runnable /扩展Thread的一些困惑和实际的线程?)。特别是,我猜测new Listener构造并启动一个线程。然后,您在第一个线程中调用addSocketChannel。因此,存在竞争条件。

selector设为静态也是一个不好的主意。

答案 1 :(得分:1)

读取工作从另一个线程读取,这是您的代码的明显问题。

public void run() {
    Set keysSet = selector.keys();

这里你从迭代器获取键集,但是没有代码在选择器上执行select()或selectNow(),所以这个set总是为空。

    Iterator i = keysSet.iterator();        
    while (i.hasNext()) {
        SelectionKey key = (SelectionKey) i.next();
    }
    if (key.isReadable()) {
        //read and do something
    }
}

这甚至没有编译,关键的'read'检查必须在while块内完成。

SelectionKey key = clientSocketChannel.register(selector,
                                                SelectionKey.OP_READ | 
                                                SelectionKey.OP_WRITE);              

两个问题:在完成此操作之前,应将通道设置为非阻塞模式,并且不应设置SelectionKey.OP_WRITE,除非您希望每次运行select时都返回该键。

如果您实际上打算进行写操作,则只应设置SelectionKey.OP_WRITE。

最后,在这里使用两个线程非常不同寻常。建议的方法是使用OP_ACCEPT将ServerSocketChannel注册到Selector,并在与读/写相同的线程上运行ServerSocket上的accept。