Updated 10 hours ago
Run ss -tan on any server and you'll see something most people scroll past: a column of states. ESTABLISHED. TIME_WAIT. CLOSE_WAIT. SYN_SENT.
These aren't just labels. They're a conversation frozen in time. Each state tells you who spoke last and what they're waiting to hear back.
What Connection States Actually Are
TCP connections are conversations between two machines. Like any conversation, they have a beginning ("hello"), a middle (actual communication), and an end ("goodbye"). Connection states mark where you are in that conversation.
UDP doesn't have states because UDP doesn't have conversations. It just shouts packets into the void and hopes they arrive. No handshake, no acknowledgment, no state to track.
But TCP guarantees delivery. To do that, both sides need to agree on what's happening at every moment. States are that agreement made visible.
The States
LISTEN — A server with its hand cupped to its ear, waiting. "I'm ready if anyone wants to talk." Every web server on port 443, every SSH daemon on port 22, starts here.
SYN_SENT — The client knocked on the door. Now it's standing outside, waiting. If it waits too long, something's wrong: the server is down, a firewall ate the knock, or the address doesn't exist.
SYN_RECEIVED — The server heard the knock and called back "who's there?" Now it's waiting for the client to finish the handshake. A thousand connections stuck here? Someone's knocking on doors and running away. That's a SYN flood—attackers leaving servers with hands extended for handshakes that will never complete.
ESTABLISHED — The conversation is happening. Data flows both ways. This is the healthy state, the one you want to see.
FIN_WAIT_1 — One side said "I'm done talking" but hasn't heard acknowledgment yet.
FIN_WAIT_2 — The goodbye was acknowledged, but the other side hasn't said their own goodbye yet.
CLOSE_WAIT — The remote side hung up. Your side acknowledged it. But your application hasn't closed the connection yet. This is a ghost—the other party left, but your application is still holding the door open, waiting for someone who's never coming back. Many connections stuck here means your application has a bug.
TIME_WAIT — The connection is over, but the system waits 30-120 seconds before forgetting it entirely. Why? Stray packets from the old conversation might still be bouncing around the Internet. TIME_WAIT prevents them from confusing a new conversation using the same port numbers.
CLOSED — Gone. The conversation never happened as far as the system is concerned.
How to Look
On Linux:
On macOS:
To count connections by state:
This shows you the shape of your server's conversations at a glance.
What the Patterns Mean
Hundreds of SYN_SENT — Your outbound connections aren't reaching their targets. Firewall? Routing problem? Dead server on the other end?
Hundreds of CLOSE_WAIT — Your application isn't cleaning up after itself. The remote sides closed their connections; your application never noticed or never responded. Fix your code.
Thousands of TIME_WAIT — Not necessarily a problem. High-traffic servers naturally accumulate these. But if you're running out of ports (typically 28,000-60,000 available), you'll need to tune TIME_WAIT duration or enable port reuse.
Stuck ESTABLISHED connections that aren't moving data — The conversation stopped but neither side admitted it. Enable TCP keepalive to detect and clean up these zombies.
SYN_RECEIVED backing up — Either you're under attack (SYN flood) or experiencing a traffic spike that's overwhelming your SYN queue. Increase the backlog. Enable SYN cookies.
UDP's Silence
UDP sockets appear in ss output, but they don't have states. There's no ESTABLISHED, no TIME_WAIT, no conversation to track. The socket is open or it isn't. Packets arrive or they don't.
This makes UDP fast—no handshake overhead, no state to maintain. It also makes UDP blind. If packets disappear, UDP doesn't know and doesn't care. Applications using UDP (DNS, video streaming, gaming) must handle reliability themselves if they need it.
The Insight
Connection states are diagnostic gold because they reveal intent. SYN_SENT means "trying to connect." CLOSE_WAIT means "forgot to hang up." TIME_WAIT means "just finished."
When something breaks, the state tells you where in the conversation the breakdown occurred. That's often enough to identify the problem—firewall, application bug, network issue, or resource exhaustion—without guessing.
The next time a service hangs, check the states. The machines are trying to tell you what went wrong.
Frequently Asked Questions About Connection States
Was this page helpful?