NIO Server无法收听客户端

时间:2022-09-05 13:16:10

Hi I am trying to implements a simple Java NIO server; which registers the socketChannel with the selector. Hence I wish to listen to client and send some response back. After the socketChannel is registered with the selector, even if client(non NIO) sends some data, Server is not able to read; howerver the key generated is still being iterated.

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

Detailed View: Server Side:

详细视图:服务器端:

**First thread**:

public void run() { while (true) {

public void run(){while(true){

    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");

NB: When done in single thread, the same program works, but when implemented in the manner mentioned above causes it not to listen to client. Please help me to resolve this issue.

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

2 个解决方案

#1


It's very difficult to see what you have done there, but it looks as if what you have marked as "second thread" is used by both threads (some confusion over implementing Runnable/extending Thread and actual threads?). In particular, I'm guessing the new Listener constructs and starts a thread. You are then calling addSocketChannel in the first thread. Therefore, there is a race condition.

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

Also it's a poor idea to make selector static.

选择器静态也是一个糟糕的主意。

#2


Read works reading from another thread, here are the obvious problems with your code.

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

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

Here you are taking the key set from the iterator, but there is no code ever doing select() or selectNow() on the selector, so this set will always be empty.

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

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

This doesn't even compile, the check for 'read' on the key must be done inside the while-block.

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

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

Two problems: The channel should be set in non-blocking mode before this is done and SelectionKey.OP_WRITE should not be set unless you want the key to be returned every time you run a select.

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

You should only set SelectionKey.OP_WRITE if you are actually planning to do a write.

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

Finally, using two threads here is very unconventional. The recommended way of doing this is to register the ServerSocketChannel to the Selector with OP_ACCEPT, and run the accept on the ServerSocket on the same thread as read/writes.

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

#1


It's very difficult to see what you have done there, but it looks as if what you have marked as "second thread" is used by both threads (some confusion over implementing Runnable/extending Thread and actual threads?). In particular, I'm guessing the new Listener constructs and starts a thread. You are then calling addSocketChannel in the first thread. Therefore, there is a race condition.

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

Also it's a poor idea to make selector static.

选择器静态也是一个糟糕的主意。

#2


Read works reading from another thread, here are the obvious problems with your code.

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

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

Here you are taking the key set from the iterator, but there is no code ever doing select() or selectNow() on the selector, so this set will always be empty.

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

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

This doesn't even compile, the check for 'read' on the key must be done inside the while-block.

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

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

Two problems: The channel should be set in non-blocking mode before this is done and SelectionKey.OP_WRITE should not be set unless you want the key to be returned every time you run a select.

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

You should only set SelectionKey.OP_WRITE if you are actually planning to do a write.

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

Finally, using two threads here is very unconventional. The recommended way of doing this is to register the ServerSocketChannel to the Selector with OP_ACCEPT, and run the accept on the ServerSocket on the same thread as read/writes.

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