Memra

Socket options and the exception family

◈ 6 cards

Set the options that actually change behaviour — SO_TIMEOUT first, then TCP_NODELAY, SO_LINGER, the buffers and SO_KEEPALIVE — and diagnose a failed connection from its exception subclass.

Nine options, one that always matters

Java exposes nine options on a client socket, and their odd names come straight from the Berkeley Unix C headers where sockets were invented: TCP_NODELAY, SO_BINDADDR, SO_TIMEOUT, SO_LINGER, SO_SNDBUF, SO_RCVBUF, SO_KEEPALIVE, OOBINLINE and IP_TOS. Most are reached through a getter/setter pair on Socket with a normal Java name — SO_BINDADDR is read-only, exposed as getLocalAddress() — and each throws SocketException if the platform's socket implementation cannot honour it.

SO_TIMEOUT is the one you set every time. setSoTimeout(ms) bounds each read; 0 is the default and means block forever. When it expires you get a SocketTimeoutException and — this is the useful part — the socket stays connected, so you may retry the read. It is the difference between a client that reports a problem and a client that hangs.

Nagle, and when to turn it off

Nagle's algorithm is on by default. It coalesces small writes: rather than putting a one-byte payload in its own packet, TCP holds it until the previous packet is acknowledged, then sends whatever accumulated. On bulk transfers this is pure win — fewer packets, less header overhead.

It is a loss when your protocol is a stream of small, latency-sensitive parcels: a game sending cursor positions, a terminal session, an interactive request/response protocol on a slow link. Every small write waits for a round trip that carries no data. setTcpNoDelay(true) turns TCP_NODELAY on, which turns the coalescing off and sends each write immediately. Note the double negative; it is a common exam trip.

Lingering, buffers, keepalives, and the rest

SO_LINGER decides what close() does with data still queued. By default close() returns at once and the TCP stack keeps trying to deliver. setSoLinger(true, 0) discards unsent data immediately. setSoLinger(true, n) makes close() block up to n seconds waiting for delivery and acknowledgment. The maximum is 65,535 seconds, and getSoLinger() returns -1 when the option is off.

SO_RCVBUF and SO_SNDBUF (setReceiveBufferSize / setSendBufferSize) are hints to the stack. They matter because maximum throughput is roughly buffer size divided by round-trip latency, so a high-latency link needs a large buffer to go fast — a fat transcontinental pipe with a small buffer stays slow no matter how much bandwidth it has. 128 KB is a common modern default.

SO_KEEPALIVE (default false) sends a probe over an idle connection, typically every two hours, so a client eventually notices a peer that died without closing. OOBINLINE governs TCP's single-byte urgent data (sendUrgentData), which Java discards by default and almost no protocol uses. IP_TOS (setTrafficClass) sets the eight-bit class-of-service field — six bits of DSCP plus two of ECN — that routers may use to prioritise, and frequently ignore. SO_REUSEADDR exists on Socket but earns its keep on the server side, in Module 7.

Diagnose from the exception type

Most Socket methods throw IOException or its subclass SocketException, and knowing only "something failed" is not enough to act. The subclasses answer which thing failed:

  • BindException — the local port is already in use, or you lack the privilege to bind it (ports below 1024).
  • ConnectException — the remote host actively refused: nothing is listening on that port, or its backlog is full.
  • NoRouteToHostException — the attempt timed out; the host is down, or a firewall is silently dropping your packets.
  • ProtocolException — extends IOException directly, not SocketException; the peer sent something that violates the specification.
  • SocketTimeoutException — extends InterruptedIOException, so it is not a SocketException. Catch it before or alongside, never expecting a SocketException clause to cover it.

Worked example — a client that says what actually went wrong

A catch (IOException ex) that prints "network error" is worthless to whoever has to fix it. Catch the subclasses most-specific first and turn each into an instruction: refused means check the port and whether the service is up; no route means check the host and the firewall; a read timeout means the server accepted you and then stalled. Same code, three different next actions.

optionsetterdefaultchange it whenSO_TIMEOUTsetSoTimeout(ms)0 = foreveralwaysTCP_NODELAYsetTcpNoDelay(true)off, Nagle onsmall urgent writesSO_LINGERsetSoLinger(on, s)off (-1)close must drainSO_RCVBUFsetReceiveBufferSize(n)about 128 KBhigh-latency linkSO_KEEPALIVEsetKeepAlive(true)falselong idle linksIP_TOSsetTrafficClass(n)0DSCP-aware networkSet SO_TIMEOUT on every client, always.
SO_TIMEOUT is the only row you should treat as mandatory. The rest are tuning: change one when you can name the symptom it fixes, and measure afterwards.
IOExceptionSocketExceptionBindExceptionport in useConnectExceptionnobody homeNoRouteToHostExceptiontimed outProtocolExceptionpeer broke specSocketTimeoutException sits elsewhere.
SocketTimeoutException is deliberately absent: it extends InterruptedIOException, not SocketException, so a catch clause for SocketException will not catch a read timeout. That surprise is worth one exam mark.

source Harold 4e ch8 §Socket Exceptions; JDK javadoc java.net.SocketTimeoutException

NORMAL ~/memra/learn/comp-348/socket-options-and-exceptions utf-8 LF