当前位置: 首页 > 知识库问答 >
问题:

Java多个OP代码的SocketChannel寄存器()从不被选择

方祺
2023-03-14

我在Java构建了一个基于选择器的系统,可以接受多个客户端。它有一个在OP_ACCEPT下注册的ServerSocketChannel,它接受()的传入连接,并再次用选择器注册结果的SocketChannel。这是一点:

ServerSocketChannel insock = ServerSocketChannel.open();
    insock.configureBlocking(false);
    insock.socket().bind(new InetSocketAddress(6789));

    Selector sel = Selector.open();
    SelectionKey joinchannel = insock.register(sel, SelectionKey.OP_ACCEPT);

    System.out.println("Ready to accept incoming connections.");

    while (true) {
        int ready = sel.selectNow();
        if (ready == 0)
            continue;
        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
        while (keyIterator.hasNext()) {
            SelectionKey key = keyIterator.next();
            if(key.isAcceptable()){
                SocketChannel newConnection = insock.accept();
                System.out.println("New client "+newConnection+" connected.");
                newConnection.configureBlocking(false);
                newConnection.register(sel, SelectionKey.OP_READ).attach(new DGPlayer());
            }

如果我为OP_READ注册了新的SocketChannel,这很好。检查isReadable()成功,读取数据。如下所示:

else if(key.isReadable()){
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(trans > 0){
                    DGPlayer client = (DGPlayer) key.attachment();
                    System.out.println(client.name+": "+msg.trim());
                }
            }
                //              else if(key.isWritable()){
//                  ByteBuffer buf = ByteBuffer.allocate(48);
//                  buf.clear();
//                  buf.put(((String)key.attachment()).getBytes());
//                  buf.flip();
//                  
//                  SocketChannel target = ((SocketChannel)key.channel());
//                  while(buf.hasRemaining()) {
//                      target.write(buf);
//                  }
//                  
//                  key.attach(null);
//              }

然而,如果我为OP_READ|OP_WRITE注册SocketChannel,什么也不会发生。据我所知,选择器从未发现该通道是可读的()或可写的()。唯一的变化是注册该通道以获得WRITE权限。

知道为什么会这样吗?

编辑-为了完整起见,这里有一些客户端代码:

while (true) {
        int readyChannels = sel.select();
        if (readyChannels == 0)
            continue;

        Set<SelectionKey> selectedKeys = sel.selectedKeys();
        Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

        while (keyIterator.hasNext()) {
            SelectionKey key = keyIterator.next();

            if (key.isReadable()) {
                ByteBuffer buf = ByteBuffer.allocate(1024);
                int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
                byte[] ba = new byte[buf.remaining()]; buf.get(ba);
                String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));

                if(msg.trim().length() != 0)
                    System.out.println("Response from server: "+msg.trim());
            }
            else if(key.isWritable() && messages.size() > 0){
                String message = messages.remove();
                ByteBuffer buf = ByteBuffer.allocate(48);
                buf.clear();
                buf.put(message.getBytes(Charset.forName("UTF-8")));

                buf.flip();

                int written = outsock.write(buf);
                while(buf.hasRemaining()){
                    outsock.write(buf);
                }

                System.out.println("Wrote '"+message+"' to server");
            }
            keyIterator.remove();
        }
    }

客户端注册服务器进行操作读取|操作写入)

共有1个答案

颜文康
2023-03-14

一般来说,如果你没有任何可以主动写入的内容,那么为OP_WRITE注册一个频道是个坏主意。大多数通道在大多数时间都可以写入,因此每次调用选择器。选择将返回而不阻塞并消耗资源。最好将写入标志添加到SelectionKey。当你准备好向频道写入内容时,删除该标志。

 类似资料:
  • 我已经尝试了大约一个小时,现在注册一个编解码器,我为我的一个课程在一个游戏中,我正在工作。该类名为。我尝试了这3个地方的代码和建议: null 下面是我用的代码: 所以我构建了一个名为的编解码器和编解码器提供程序,那么我做错了什么,怎么会这么复杂呢?我是不是漏掉了什么?它似乎比它需要的要复杂得多。如果你需要更多的代码,请询问。谢了。 Edit:我得到的确切错误是另外,我正试图解析一个项数组,我是否

  • 在wikipedia x86调用约定中,它说对于Microsoft x64调用约定: 寄存器RBX、RBP、RDI、RSI、RSP、R12、R13、R14和R15被视为非易失性(被叫方保存)。 但对于System V AMD64 ABI: 如果被调用方希望使用寄存器RBX、RBP和R12-R15,则必须在将控制权返回给调用方之前恢复它们的原始值。 我的问题是,在不同的平台上调用约定是不是不同的?(

  • 考虑以下x86程序集: 序列结束时,rax的值与输入时的值相同,但从CPU的角度来看,其值取决于从内存加载到rcx的值。特别是,在该加载和两个异或指令完成之前,不会开始后续使用rax。 有什么方法可以比两个异或序列更有效地实现这种效果,例如,使用单个单uop单周期延迟指令?如果某个常量值需要在序列之前设置一次(例如,有一个零寄存器),则可以。

  • 我正在为一个操作系统分配编写内联汇编代码。我有一些关于内联汇编和gcc编译器将其转换为机器代码的问题。 null

  • Vim提供了许多寄存器。可以将这些寄存器用作多个剪贴板。使用多个文件时,此功能非常有用。在本章中,将讨论以下主题内容 - 复制寄存器中的文本 粘贴寄存器中的文本 列出可用的寄存器 寄存器类型 1. 复制寄存器中的文本 对于复制,可以使用普通的命令,即并将其存储在寄存器中,可以使用以下语法 - 例如,要复制寄存器中的文本,请使用以下命令 - 2. 粘贴寄存器中的文本 从寄存器粘贴文本 - 例如,下面

  • 当前的Perl 5虚拟机是一台堆栈机器。 它通过将操作保持在堆栈上来传递操作之间的值。 操作将值加载到堆栈上,执行他们需要执行的操作并将结果放回堆栈。 这很容易使用,但速度很慢。 要将两个数字相加,您需要执行三次堆栈推送和两次堆栈弹出。 更糟糕的是,堆栈必须在运行时增长,这意味着在您不想分配内存时分配内存。 因此,Parrot将打破虚拟机的既定传统,并使用寄存器架构,更类似于真实硬件CPU的架构。