Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Using the Sun Java VM 1.5 or 1.6 on Windows, I connect a non-blocking socket. I then fill a ByteBuffer with a message to output, and attempt to write() to the SocketChannel.

I expect the write to complete only partially if the amount to be written is greater than the amount of space in the socket's TCP output buffer (this is what I expect intuitively, it's also pretty much my understanding of the docs), but that's not what happens. The write() always seems to return reporting the full amount written, even if it's several megabytes (the socket's SO_SNDBUF is 8KB, much, much less than my multi-megabyte output message).

A problem here is that I can't test the code that handles the case where the output is partially written (registering an interest set of WRITE to a selector and doing a select() to wait until the remainder can be written), as that case never seems to happen. What am I not understanding?

like image 543
jpdaigle Avatar asked Jan 25 '23 02:01

jpdaigle


1 Answers

I managed to reproduce a situation that might be similar to yours. I think, ironically enough, your recipient is consuming the data faster than you're writing it.

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);
  }
}

If you run the above server and then make the above client attempt to write 10 chunks of 128 kB of data, you'll see that every write operation writes the whole buffer without blocking. However, if you modify the above server not to read anything from the connection, you'll see that only the first write operation on the client will write 128 kB, whereas all subsequent writes will return 0.

Output when the server is reading from the connection:

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

Output when the server is not reading from the connection:

to write: 131072, written:  131072
to write: 131072, written:  0
to write: 131072, written:  0
...  
like image 184
Alexander Avatar answered Feb 01 '23 23:02

Alexander