高级套接字编程 - 服务器到客户端的额外数据传输问题

时间:2010-08-10 12:59:40

标签: java sockets nio

这里有一台服务器和一台客户端。并且通过可选择的频道维持了通信。 喜欢 - 服务器---

  SelectionKey selectKey = channel.register(this.selector,

        SelectionKey.OP_ACCEPT);

while (selectKey.selector().select() > 0) {

    Set<SelectionKey> selectedKeys = this.selector.selectedKeys();

    Iterator<SelectionKey> iterator = selectedKeys.iterator();

    while (iterator.hasNext()) {

        SelectionKey key = iterator.next();

        iterator.remove();

        if (key.isAcceptable()) {

            ServerSocketChannel nextChannel = (ServerSocketChannel) key

                        .channel();
            SocketChannel newChannel = nextChannel.accept();

            newChannel.configureBlocking(false);

            if (!newChannel.isRegistered()) {

                SelectionKey selectionKey = newChannel.register(

                     this.selector, SelectionKey.OP_READ

                | SelectionKey.OP_WRITE);

                selectionKey.attach(newChannel);

            }

        } else if (key.isWritable()) {

             SocketChannel attachment1 = (SocketChannel)key.attachment();
             ByteBuffer writeBuffer = ByteBuffer.wrap("Hello".getBytes());

                attachment1.write(writeBuffer);

                System.out.print("Written");
            }
         }
      } 

客户:

   InetSocketAddress isa = new InetSocketAddress(InetAddress
            .getLocalHost(), 4444);
    SocketChannel sc = null;

    try {

        while (true) {

            Thread.sleep(10000);
            sc = SocketChannel.open();
            sc.connect(isa);
            ByteBuffer byteBuffer = ByteBuffer.allocate(BUFSIZE);
            int nbytes = sc.read(byteBuffer);
            byteBuffer.flip();
            dbuf.flip();
            CharBuffer cb = decoder.decode(byteBuffer);
            System.out.println(isa + " : " + cb);

        }

问题是每次客户端读取数据达到客户端缓冲区限制的全部大小而不是发送数据限制。

2 个答案:

答案 0 :(得分:5)

这是TCP套接字的工作方式 - 它们是一个字节流,而不是一系列消息。

您需要设计更高级别的协议,以便接收方在收到字节后重新发现消息边界。

答案 1 :(得分:1)

您的代码中有一些非常奇怪的东西。难怪它是行为不端。

  1. 从ServerSocketChannel接受的频道是全新的,因此检查它是否已注册是浪费时间。它没有注册。

  2. 将频道附加到选择键是没有意义的:SelectionKey已经有了channel()方法。附件应该是某种会话上下文对象,包含会话的缓冲区和您需要保留的任何其他状态。

  3. 您的服务器永远不会从任何频道读取。所以它无法检测到有序断开,所以它永远不会关闭一个频道,所以它必须继续处理它所接受的每个频道。

  4. 您的服务器不检查write()的结果。它可以是从零到buffer.remaining()的任何内容。

  5. 每次读取分配一个新的ByteBuffer非常浪费。它们的制作成本相对较高。您应该为每个频道创建一个并在频道的生命周期中使用它。也许两个,一个用于阅读,一个用于写作。

  6. 您永远不会关闭客户端中的SocketChannel,因此您将使用空闲连接来淹没服务器。

  7. 您滥用OP_WRITE。 OP_WRITE一直就绪,除非套接字的发送缓冲区已满。您应该(a)只在准备就绪时写入,(b)检查返回值,(c)如果为零则注册OP_WRITE的通道,(d)当你得到OP_WRITE时,做一个写,并注销 OP_WRITE,除非你有另一个零。目前,通过将服务器写入每个频道,您也会压倒服务器,而您甚至无法通过客户端阅读所有这些写入。

  8. 在flip()/ decode()序列之后,你必须压缩()缓冲区(如果它将被重复使用),应该如此。