为什么即使在非阻塞套接字上,SocketChannel写入也总是完整的?
-
03-07-2019 - |
题
在Windows上使用Sun Java VM 1.5或1.6,我连接了一个非阻塞套接字。然后我填写 ByteBuffer
并输出一条消息,然后尝试 write()
到SocketChannel。
如果要写入的数量大于套接字TCP输出缓冲区中的空间量,我希望写入只能部分完成(这是我直观的预期,这也是我对 docs ),但事实并非如此。 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
。 / p>
服务器从连接读取时的输出:
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)循环中,你最终会得到一个成功的写入,其值小于请求的值。
您在哪里发送数据?请记住,网络充当缓冲区,其大小至少与SO_SNDBUF和接收器的SO_RCVBUF相等。如Alexander所述,接收器将其添加到阅读活动中,您可以获得大量数据。