What SO_LINGER does
SO_LINGER is a socket option (setsockopt) that controls how
close() behaves when unsent data remains on the socket.
- SO_LINGER decides how the socket behaves when the application calls close() and there is unsent data in the socket’s send buffer.
- Unsent data = bytes written by the application but not yet transmitted and acknowledged by the peer.
Behavior in Client Scenario
- Client workflow: Send request → Wait for complete response → Call close().
- At the point of close():
- All client request bytes have been transmitted and ACKed.
- Send buffer is empty.
- There is no “unsent data” for SO_LINGER to discard.
- Result:
- With SO_LINGER(0), the client still sends RST instead of FIN, but no client data is lost.
- The server may log the abrupt reset, but functionally it is harmless for stateless APIs.
Normal Close (default, no linger set):
- Client: FIN →
- Server: ACK → FIN →
- Client: ACK
Connection passes through FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT.
✅ Characteristics:
- Graceful 4-way handshake.
- States traversed: FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT (client) and CLOSE_WAIT → LAST_ACK (server).
- Guarantees reliable delivery of all data.
SO_LINGER(0) (Abortive Close) :
- Client: RST →
- Server: Connection dropped immediately
All intermediate states skipped → both sides move to CLOSED instantly.
✅ Characteristics:
- Instant teardown.
- Skips FIN/ACK handshake, TIME_WAIT, CLOSE_WAIT, LAST_ACK.
- Peer sees abrupt RST.
- Any unsent data is discarded (not applicable in our stateless scenario).
Practical Implications of SO_LINGER(0)
- ✅ No risk of data loss here (request fully sent, response fully received).
- ✅ Good for short-lived, stateless API calls — avoids lingering sockets.
- ⚠️ Server logs may show RST instead of FIN.
- ⚠️ Should not be used in protocols requiring graceful close or guaranteed delivery after close().
SO_LINGER(5)
Case A: All data delivered before timeout
✅ Behavior:
- close() blocks until handshake completes.
- Graceful 4-way close, just like normal.
- Application knows delivery succeeded before returning.
Case B: Timeout expires (no ACK from server within 5s)
❌ Behavior:
- Data not acknowledged → stack aborts with RST.
- Connection torn down abruptly.
- Peer sees reset instead of FIN.
🔑 Summary of SO_LINGER(5)
- Best case: Works like normal close, but close() blocks until data is ACKed.
- Worst case: After 5s timeout, behaves like SO_LINGER(0) (RST abort).
- Useful when the application must know if the peer ACKed data before completing close().
✅ Conclusion
- In stateless client-server flow, SO_LINGER(0) is acceptable.
- It allows instant connection teardown with no data loss, since the request/response exchange is already complete.
- The only visible impact: the server sees an RST instead of a normal FIN handshake.
1. Definition of unsent data (in TCP/SO_LINGER context)
- When a client calls write() (or send()) on a TCP socket, data goes into the socket’s send buffer.
- Unsent data = bytes that have not yet been transmitted and acknowledged by the peer.
- SO_LINGER controls what happens to this unsent data when close() is called:
- SO_LINGER(0) → discard immediately, send RST.
- SO_LINGER>0 → try to send within timeout.
- default (SO_LINGER not set) → normal FIN handshake.
2. How it applies to stateless scenario
- Client flow: send request → wait → receive full response → close.
- At the point of close():
- All client request bytes have been transmitted and acknowledged by the server.
- There are no pending bytes in the client send buffer.
- Therefore, the “unsent data” that SO_LINGER refers to does not exist in your scenario.
In client workflow, the client only calls close() after sending the complete request and receiving the full server response. At this point, the socket’s send buffer is empty, so there is no unsent data. SO_LINGER(0) will still close the socket abruptly, but it does not result in any loss of transmitted data.
- By default (no linger or SO_LINGER disabled):
- close() just queues a graceful shutdown.
- TCP tries to deliver any unsent data and perform the normal 4-way FIN/ACK close.
- The application returns from close() quickly, but the actual TCP teardown may still be in progress.
- If SO_LINGER is enabled with a timeout >0 (e.g., 5 sec):
- close() becomes blocking until either:
- All unsent data is delivered and ACKed, and connection closes gracefully, or
- The timeout expires → then connection is reset (RST).
- If SO_LINGER is set with timeout = 0 (i.e., SO_LINGER(0)):
- close() causes an immediate abortive close.
- Any unsent data is discarded, and the stack sends RST instead of FIN.
- This tears down the connection instantly.
🔹 Can we use SO_LINGER(0)?
- Yes, it’s a valid, documented use.
- But it changes semantics: instead of a graceful shutdown, we’re forcing an abortive close.
- This is typically used when:
- We don’t care about undelivered data.
- We want to immediately free up resources / ports.
- We need to ensure the peer can’t reuse half-open connections.
When client call close() API , how it behaves under different SO_LINGER settings, including the packets exchanged and the practical use cases.
Mode | Packets on Wire | Behavior of close() |
Pros | Cons | Typical Use |
---|---|---|---|---|---|
Normal close (default, no SO_LINGER ) |
App calls close()
→ stack sends FIN
→ peer ACK
→ peer FIN
→ local ACK
(4-way close) |
close() returns immediately, TCP teardown
continues in background |
✅ Graceful shutdown ✅ Ensures data delivery ✅ Peer sees clean close |
❌ Leaves socket in TIME_WAIT ❌ Connection cleanup takes longer |
General case (most apps) |
SO_LINGER enabled, timeout > 0 (e.g. 5 sec) |
On close() : TCP waits until unsent data is
ACKed, then does FIN/ACK
exchange. If timeout expires → sends RST |
close() blocks until either
data is delivered or timeout expires |
✅ App knows whether data was delivered ✅ Useful in transactional protocols |
❌ Blocks calling thread ❌ If timeout, abrupt RST |
When you must confirm data delivery before returning from
close() |
SO_LINGER(0) (timeout = 0) |
Immediately sends RST, skipping FIN/ACK handshake | close() returns immediately, and connection
is torn down instantly |
✅ Frees resources instantly ✅ Avoids half-open states |
❌ Any unsent data is discarded ❌ Peer sees abrupt reset (may log error) ❌ Not graceful |
Emergency cleanup, abortive close, broken peers (like M400
not ACKing FIN ) |
Explanation
- As soon as the client calls close() with SO_LINGER(0):
- TCP stack sends RST immediately, discarding any unsent data.
- Client socket transitions instantly to CLOSED.
- Server receives RST:
- Drops the half-open connection immediately.
- Moves directly to CLOSED.
- No FIN/ACK handshake occurs; there is no FIN_WAIT, CLOSE_WAIT, LAST_ACK, or TIME_WAIT on either side.
✅ Key difference vs normal 4-way close:
- All intermediate states like FIN_WAIT-1, FIN_WAIT-2, TIME_WAIT, CLOSE_WAIT, LAST_ACK are skipped.
- Connection is torn down immediately.
TCP Buffer : When TCP documentation says “unsent data is discarded,” it refers to **data in the client’s send buffer that the TCP stack hasn’t physically put on the wire yet.
In Stateless scenario, Using SO_LINGER(0) is acceptable because:
- The client already sent the request(all transaction payload ) and received the response, so there’s no risk of losing client data.
- Client has no pending writes in the TCP send buffer. Therefore, there is no unsent data at the moment of calling close().
- The connection is stateless, and each transaction opens a new connection anyway, so skipping the graceful FIN/ACK handshake doesn’t break application logic.
- The only downside is the server may log an RST instead of a normal FIN, which is usually harmless for stateless APIs.
SO_LINGER(0) Impact
- Causes immediate TCP reset (RST) instead of normal FIN handshake.
- “Unsent data” refers to pending client-side writes, which in your case is already sent, so nothing is actually lost.
- Client sees no issue; server may log an abrupt reset.
No comments:
Post a Comment