Java NIO服务器无法侦听客户端

Java NIO服务器无法侦听客户端,java,tcp,nio,Java,Tcp,Nio,嗨,我正在尝试实现一个简单的JavaNIO服务器;将socketChannel注册到选择器。因此,我希望听取客户的意见,并发送一些回复。在向选择器注册socketChannel之后,即使客户端(非NIO)发送一些数据,服务器也无法读取;然而,生成的密钥仍在迭代中 详细视图:服务器端: **First thread**: 公开募捐{ while(true){ 注意:当在单线程中完成时,相同的程序可以工作,但当以上述方式实现时,会导致它不侦听客户端。 请帮助我解决此问题。很难看到您在那里做了什么,

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

详细视图:服务器端:

**First thread**:
公开募捐{ while(true){

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

很难看到您在那里做了什么,但看起来您标记为“第二个线程”的内容似乎被两个线程使用(在实现
可运行
/extensing
线程
和实际线程方面存在一些混淆?)。特别是,我猜测
新侦听器
构造并启动一个线程。然后在第一个线程中调用
addSocketChannel
。因此,存在竞争条件


另外,将选择器设置为静态也是一个糟糕的主意。

从另一个线程读取作品,下面是代码的明显问题

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
    }
}
这甚至不会编译,对键的“read”检查必须在while块内完成

SelectionKey key = clientSocketChannel.register(selector,
                                                SelectionKey.OP_READ | 
                                                SelectionKey.OP_WRITE);              
两个问题:在完成此操作之前,应将通道设置为非阻塞模式,并且不应设置SelectionKey.OP_WRITE,除非您希望在每次运行select时返回该键


如果您确实计划写入,则只应设置SelectionKey.OP_WRITE


最后,在这里使用两个线程是非常不传统的。建议使用OP_ACCEPT将ServerSocketChannel注册到选择器,并在ServerSocket上以读/写相同的线程运行ACCEPT。

嗨,Tom,抱歉,代码不清楚。第一个线程在另一个类中运行,该类具有接受socketchannel并将其传递给注册。请告诉我应该进行哪些修改。谢谢。看来可能最短的修复方法是将对addSocketChannel的调用移动到侦听器构造函数(并在启动线程之前调用它)。但是,你真的需要整理你的线程。嗨,汤姆,你能给我一个学习和改进NIO线程的指针吗?另外,你能告诉我是否有任何替代静态选择器的方法,不能让它与volatile一起工作。有没有比这更好的方法?我经常看到Rox教程提到:你还可以研究(完全免费复制)到Naga(NIO包装库)的源代码在naga.googlecode.com上,该实现处理了许多常见问题,如部分读写、写后关闭/关闭、异步断开连接、数据包转换等。我想知道,如今,人们仍然使用6年前的Java-1.4编写风格来编写迭代器。旧的Java书籍和教程是否仍然如此普遍让我很难过。非常感谢Noji,我可以让代码运行。我同意你关于线程的概念是非传统的,但这只是我尝试看到性能优化。你能告诉我如何在读取数据后将key.isReadable标记为false吗?通常你会在读取后使用iterator.remov从集合中删除keye()。我似乎记得,否则每次执行select()时,它都会显示。在删除()之后,可以保证在有数据可供再次读取之前,密钥不会是所选密钥的一部分。当然,如果您想关闭密钥的读取兴趣(即,您不想在此连接上继续读取),您应该改为使用key.interesttops(int-ops)来设置此键的新兴趣(例如,0表示既不读也不写)。
Selector.keys()
返回已注册的键集,而不是就绪键,因此您是否调用过
select()
没有任何区别。正确。基本问题…应该是selectedKeys()如果您实际执行了返回零的写入操作,则只应设置SelectionKey.OP_WRITE,因此必须将其延迟到套接字发送缓冲区中有空间为止。否则,您只需写入。
SelectionKey key = clientSocketChannel.register(selector,
                                                SelectionKey.OP_READ | 
                                                SelectionKey.OP_WRITE);