45

If I am only WRITING to a socket on an output stream, will it ever block? Only reads can block, right? Someone told me writes can block but I only see a timeout feature for the read method of a socket - Socket.setSoTimeout().

It doesn't make sense to me that a write could block.

user207421
  • 305,947
  • 44
  • 307
  • 483
jbu
  • 15,831
  • 29
  • 82
  • 105
  • The fact that there isn't a timeout feature isn't itself an indication that it doesn't block. One indication that it does is the lack of a return value: if it doesn't block, why doesn't it return the number of bytes actually written? Another indication is given by just trying it. – user207421 Apr 17 '15 at 23:01

2 Answers2

55

If I am only WRITING to a socket on an output stream, will it ever block? Only reads can block, right?

A write on a Socket can block too1. The OS will only buffer a certain amount of untransmitted (or transmitted but unacknowledged) data. If you write stuff faster than the remote app is able to read it, the socket will eventually back up and your write calls will block.

It doesn't make sense to me that a write could block.

An OS kernel is unable to provide an unlimited amount of memory for buffering unsent or unacknowledged data. Blocking in write is the simplest way to deal with that.


Responding to these followup questions:

So is there a mechanism to set a timeout for this? I'm not sure what behavior it'd have...maybe throw away data if buffers are full? Or possibly delete older data in the buffer?

There is no mechanism to set a write timeout on a java.net.Socket. There is a Socket.setSoTimeout() method, but it affects accept() and read() calls ... and not write() calls. Apparently, you can get write timeouts if you use NIO, non-blocking mode, and a Selector, but this is not as useful as you might imagine.

A properly implemented TCP stack does not discard buffered data unless the connection is closed. However, when you get a write timeout, it is uncertain whether the data that is currently in the OS-level buffers has been received by the other end ... or not. The other problem is that you don't know how much of the data from your last write was actually transferred to OS-level TCP stack buffers. Absent some application level protocol for resyncing the stream2, the only safe thing to do after a timeout on write is to shut down the connection.

By contrast, if you use a datagram socket (e.g. UDP), write() calls won't block for a significant3 length of time. But the downside is that if there are network problems or the remote application is not keeping up, messages will be dropped on the floor with no notification to either end. In addition, you may find that messages are sometimes delivered to the remote application out of order. It will be up to you (the developer) to deal with these issues.

1 - But not a DatagramSocket.
2 - It is theoretically possible to do this, but for most applications it makes no sense to implement an additional resyncing mechanism on top of an already reliable (to a point) TCP/IP stream. And if it did make sense, you would also need to deal with the possibility that the connection closed ... so it would be simpler to assume it closed.
3 - The javadocs don't say that send won't block at all. But I would expect that the OS would discard out-going datagrams rather than blocking.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
7

The only way to do this is to use NIO and selectors.

See the writeup from the Sun/Oracle engineer in this bug report: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4031100

Madjosz
  • 509
  • 1
  • 5
  • 13
Noel Grandin
  • 3,143
  • 25
  • 17