1

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

详细视图:服务器端:

**First thread**:

公共无效运行(){而(真){

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

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

4

2 回答 2

1

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

selector制作静态也是一个糟糕的主意。

于 2009-05-25T12:40:33.413 回答
1

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

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

在这里,您从迭代器中获取键集,但没有代码在选择器上执行 select() 或 selectNow(),因此该集将始终为空。

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

这甚至不能编译,必须在 while 块内完成对键的“读取”检查。

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

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

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

最后,这里使用两个线程是非常规的。这样做的推荐方法是使用 OP_ACCEPT 将 ServerSocketChannel 注册到 Selector,并在与读/写相同的线程上运行 ServerSocket 上的接受。

于 2009-05-25T18:27:37.433 回答