繁体   English   中英

为什么即使在非阻塞套接字上,SocketChannel写入也总是完整的?

[英]Why do SocketChannel writes always complete for the full amount even on non-blocking sockets?

在Windows上使用Sun Java VM 1.5或1.6,我连接一个非阻塞套接字。 然后我用一个消息填充ByteBuffer输出,并尝试write()到SocketChannel。

如果要写入的数量大于套接字TCP输出缓冲区中的空间量,我希望写入只能部分完成(这是我所期望的直观,这也是我对文档的理解),但那不是什么发生。 write()似乎总是返回报告写入的全部数量,即使它是几兆字节(套接字的SO_SNDBUF是8KB,远远小于我的多兆字节输出消息)。

这里的问题是我无法测试处理部分写入输出的情况的代码(将WRITE的兴趣集注册到选择器并执行select()以等待直到写入其余部分),因为案件似乎永远不会发生。 我不明白的是什么?

我设法重现了一个可能类似于你的情况。 我认为,具有讽刺意味的是,您的收件人比您编写数据的速度更快。

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");

    final InputStream in = cs.getInputStream();
    final byte[] tmp = new byte[64 * 1024];
    while (in.read(tmp) != -1);

    Thread.sleep(100000);
  }
}



import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class MyNioClient {
  public static void main(String[] args) throws Exception {
    final SocketChannel s = SocketChannel.open();
    s.configureBlocking(false);
    s.connect(new InetSocketAddress("localhost", 12345));
    s.finishConnect();

    final ByteBuffer buf = ByteBuffer.allocate(128 * 1024);
    for (int i = 0; i < 10; i++) {
      System.out.println("to write: " + buf.remaining() + ", written: " + s.write(buf));
      buf.position(0);
    }
    Thread.sleep(100000);
  }
}

如果您运行上述服务器,然后让上面的客户端尝试写入10个128 kB的数据块,您将看到每个写操作都会写入整个缓冲区而不会阻塞。 但是,如果您修改上述服务器而不是从连接中读取任何内容,您将看到只有客户端上的第一个写操作将写入128 kB,而所有后续写入将返回0

服务器从连接读取时的输出:

to write: 131072, written:  131072
to write: 131072, written:  131072
to write: 131072, written:  131072
...

服务器未从连接读取时的输出:

to write: 131072, written:  131072
to write: 131072, written:  0
to write: 131072, written:  0
...  

我一直在使用Java中的UDP,并且在Java NIO的一般情况下看到了一些非常“有趣”且完全没有文档记录的行为。 确定发生了什么的最好方法是查看Java附带的源代码。

我也会高度谨慎地说你可能会在任何其他JVM实现中找到更好的实现,例如IBM,但我不能保证不自己看它们。

我无法在任何地方找到它,但是IIRC [1],send()保证要么a)完全发送提供的缓冲区,要么b)失败。 它永远不会部分完成发送。

[1]我已经编写了多个Winsock实现(用于Win 3.0,Win 95,Win NT等),因此这可能是特定于Winsock(而不是通用套接字)的行为。

我将实现信念的大跃进,并假设Java的底层网络提供程序与C相同...... O / S为每个套接字分配的不仅仅是SO_SNDBUF 我敢打赌,如果你把你的发送代码放在for(1,100000)循环中,你最终会得到一个成功的写入值小于请求的值。

你真的应该看看像MINAGrizzly这样的NIO框架。 我在企业聊天服务器上使用MINA取得了巨大成功。 它也用于Openfire聊天服务器。 Grizzly用于Sun的JavaEE实现。

你在哪里发送数据? 请记住,网络充当缓冲区,其大小至少与SO_SNDBUF和接收器的SO_RCVBUF相等。 如Alexander所述,接收器将其添加到阅读活动中,您可以获得大量数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM