4

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

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

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

4

6 回答 6

7

我设法重现了可能与您的情况相似的情况。我认为,具有讽刺意味的是,您的收件人使用数据的速度比您编写数据的速度要快。

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
...  
于 2008-10-01T16:12:16.763 回答
2

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

我还敢打赌,您可能会在任何其他 JVM 实现(例如 IBM 的实现)中找到您正在寻找的更好的实现,但如果不亲自查看它们,我不能保证这一点。

于 2008-10-01T15:27:17.260 回答
0

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

于 2008-10-01T15:16:08.957 回答
0

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

于 2008-10-02T02:36:16.060 回答
0

你将数据发送到哪里?请记住,网络充当缓冲区,其大小至少等于您的 SO_SNDBUF 加上接收器的 SO_RCVBUF。如亚历山大所说,将其添加到接收者的阅读活动中,您可以获得大量数据。

于 2008-10-02T17:30:57.143 回答
0

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

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

于 2012-04-02T08:56:10.830 回答