SSH uses TCP’s reliable, ordered, and error-checked delivery to ensure your commands and data arrive intact, even if the network is a bit messy.

Let’s see it in action. Imagine you have a server appserver.example.com and you want to SSH into it.

ssh user@appserver.example.com

When you run this, your SSH client initiates a TCP connection to the SSH server (usually listening on port 22) on appserver.example.com. This isn’t magic; it’s the standard TCP three-way handshake:

  1. Client sends SYN: Your SSH client sends a TCP segment with the SYN (synchronize) flag set to the server’s port 22. This says, "I want to start a connection."
  2. Server sends SYN-ACK: If the server is listening and ready, it responds with a TCP segment that has both the SYN and ACK (acknowledge) flags set. This says, "Okay, I heard you, and I’m ready to synchronize too."
  3. Client sends ACK: Your client receives the SYN-ACK and sends back a final ACK segment. This says, "Great, connection established."

Once this handshake is complete, the TCP connection is live. Now, SSH layers its own security and protocols on top of this established TCP pipe. Your keystrokes, commands, and the output from the server are all broken down into TCP segments, sent across the network, and reassembled by the receiving end. TCP guarantees that if a segment is lost, it will be retransmitted, and if segments arrive out of order, they’ll be reordered before SSH sees them.

The problem SSH solves, and why it needs TCP, is twofold:

  • Security: Before SSH, tools like telnet and rsh sent credentials and data in plain text, making them vulnerable to eavesdropping. SSH encrypts everything.
  • Reliability: Network connections can be unreliable. TCP provides the robust transport layer that ensures your session doesn’t get garbled or drop packets unexpectedly.

Internally, after the TCP connection is set up, SSH establishes a secure channel. It negotiates encryption algorithms, key exchange methods, and authentication mechanisms. This secure channel then acts like a virtual pipe through which your commands and data flow. When you type ls -l, your client encrypts this string, breaks it into TCP segments, and sends them. The server receives the segments, reassembles the encrypted command, decrypts it, executes ls -l, encrypts the output, breaks it into segments, and sends it back. Your client receives, decrypts, and displays the output.

The configuration for SSH’s TCP behavior is mostly handled by the sshd_config file on the server and ssh_config on the client. You’ll typically see directives like:

Port 22
ListenAddress 0.0.0.0

In sshd_config: Port 22 tells the SSH daemon to listen for incoming TCP connections on port 22. ListenAddress 0.0.0.0 means it will accept connections on all available network interfaces.

On the client side, in ssh_config, you might have:

Host appserver.example.com
  Hostname appserver.example.com
  User myuser
  Port 22
  ServerAliveInterval 60

Here, ServerAliveInterval 60 is interesting. It tells your SSH client to send a "keep-alive" message (an empty SSH packet) to the server every 60 seconds if no data has been sent. This isn’t an SSH feature itself, but rather SSH using TCP to send these small packets. The purpose is to prevent intermediate network devices (like firewalls or NAT gateways) from closing idle TCP connections. If the server doesn’t respond to a keep-alive, the client assumes the connection is dead and will disconnect, rather than leaving you with a seemingly active but broken session.

The underlying TCP parameters, like window sizes, retransmission timeouts, and congestion control algorithms, are usually managed by the operating system’s network stack. SSH relies on these defaults or any system-wide tuning you might have done. You don’t typically configure TCP window sizes directly within SSH, but you might tune them at the OS level for performance. For instance, on Linux, you might adjust net.ipv4.tcp_rmem and net.ipv4.tcp_wmem in /etc/sysctl.conf to influence how much memory the kernel allocates for TCP receive and send buffers, which can impact throughput for high-latency or high-bandwidth links.

What most people don’t realize is that the entire SSH session, including the authentication and command execution, is fundamentally a stream of bytes over a TCP connection. The encryption and protocol negotiation happen after TCP has done its job of establishing a reliable, ordered channel. If TCP fails – if it can’t establish a connection or keeps dropping packets faster than it can retransmit them – SSH will fail, often with cryptic errors like "Connection refused" or "Broken pipe," because the underlying transport isn’t working.

Once you’ve mastered SSH over TCP, you’ll naturally want to explore SSH tunneling and port forwarding, which leverage this established TCP connection to route traffic for other applications.

Want structured learning?

Take the full Tcp course →