Channels and flow control
Everything that carries data over an SSH connection is a channel (RFC 4254): each command, shell and SFTP session, each tunnel and forwarded connection. Many channels share one connection at once, each with its own flow control. Knowing this explains when operations run in parallel, why unread output stalls a program, and what the server's limits mean.
Channel types
| Channel type | Opened by | Created by |
|---|---|---|
session |
client | RunCommandAsync, StartCommandAsync, StartShellAsync, StartSubsystemAsync, OpenSftpAsync, SCP transfers |
direct-tcpip |
client | OpenTcpTunnelAsync, each connection to a local forwarder or SOCKS proxy, CreateHttpConnectCallback, ProxyJump hops |
direct-streamlocal@openssh.com |
client | OpenUnixSocketTunnelAsync |
forwarded-tcpip |
server | each connection to a remote forwarding (ForwardRemotePortAsync, ForwardRemoteDynamicPortAsync) |
forwarded-streamlocal@openssh.com |
server | each connection to a remote Unix-socket forwarding |
auth-agent@openssh.com |
server | each use of a forwarded agent |
Channels the server opens are accepted only when you asked for them (a forwarding, agent forwarding); anything else is refused.
Flow control
Each side of a channel grants the other a window: how many bytes it may send before it must wait. SshSharp grants 2 MiB per channel and tops the window up as your code consumes data. If your code stops reading, the window runs out and the server stops sending on that channel, which pushes back all the way to the program on the server:
sequenceDiagram
participant P as Program on the server
participant S as SSH server
participant C as SshSharp
participant R as Your code
P->>S: writes output
S->>C: CHANNEL_DATA (within the window)
C->>R: StandardOutput.ReadAsync returns data
C->>S: WINDOW_ADJUST (as data is consumed)
Note over R: your code stops reading
S->>C: CHANNEL_DATA until the window is used up
S--xP: pipe full: the program blocks on write
R->>C: reads again
C->>S: WINDOW_ADJUST
S->>C: CHANNEL_DATA resumes
Consequences:
- Memory is bounded. A channel buffers at most its window, however fast the server sends.
- stdout and stderr share one window. Reading only one of them stalls the program once the other has filled the window; read both, as Stream large output shows.
- Throughput per channel is at most one window per round trip. On a 100 ms link that is about 20 MiB/s per channel, which is
why SFTP's
Segmentsuses several channels. - Channels do not block each other. A stalled channel does not slow the others on the same connection.
Limits
- Sessions per connection. Servers limit open session channels (sshd's
MaxSessions, 10 by default). Commands, shells and SFTP sessions count; tunnels do not. - Packet size. SshSharp accepts packets up to 256 KiB and asks for at most 32 KiB of data per channel packet.
- Forwarded connections. At most
SshClientOptions.MaxForwardedConnections(1,000) channels opened by the server at a time. - Idle channels.
SshClientOptions.ChannelTimeoutscloses channels of a given type that carry no data for a while (OpenSSH'sChannelTimeout), without closing the connection.
Closing
A channel closes when either side closes it: your code disposes the session or stream, the program on the server exits, or the
connection ends. Half-close is supported: CloseInputAsync sends end-of-file on your side while output keeps coming, and forwarded
connections pass a half-close through as TCP does.