Library / SDK
xtaci/smux avatar
xtaci/smux

xtaci/smux: Stream Multiplexing for Go Connections

A stream multiplexing library for Golang with minimal memory usage.

1,612 stars233 forksGoMIT

At a glance

What is it?
xtaci/smux adds stream-oriented connections over a reliable, ordered transport such as TCP or KCP. It is aimed at Go developers who need bounded buffering, congestion control, and traffic shaping, although it adds framing and scheduling work to each transport connection.
Who is it for?
Adopt smux when an application needs several logical net.Conn streams over a reliable, ordered transport and can accept smux-specific framing and flow-control behavior. Do not choose it when a raw TCP connection is sufficient or when the underlying transport does not provide the required reliability and ordering.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 161 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why smux sits above TCP or KCP

Smux solves a transport composition problem. A single reliable, ordered connection is often exposed by a lower-level transport as one byte stream, while an application or intermediate protocol needs several independent logical streams. Smux accepts an existing io.ReadWriteCloser, creates one Session around it, and lets that session open or accept multiple Stream objects. Each stream implements net.Conn, so callers can use familiar connection-oriented reads, writes, and closes without building a second stream protocol.

The library is designed for transports such as TCP or KCP that already guarantee reliability and ordering. Smux does not describe itself as a replacement for those guarantees. The ordering also matters for deployment: opening a stream does not create another network connection, because all streams share the underlying connection. This can simplify connection management when the upper layer needs stream separation, but it does not provide an independent failure domain. A failure of the underlying connection affects every stream in that session.

The intended users are Go engineers working with custom transports, proxy layers, framed protocols, or applications that were originally designed for kcp-go. Smux was originally created to support connection management in kcp-go. It is less appropriate for a service that only needs ordinary TCP connections, because the library adds a frame layer, session state, buffers, and scheduling decisions without adding stream capability by itself.

Smux Session, Stream, and Frame processing

The repository describes three main layers. Session owns the underlying io.ReadWriteCloser, manages stream creation and acceptance, and controls the shared receive buffer. Stream represents one logical net.Conn within that session. Frame is the wire unit exchanged through the underlying connection. This separation lets the session coordinate memory across streams instead of giving every stream an independent, potentially large receive allowance.

The documented wire format is compact. Each frame begins with an 8-byte header consisting of a one-byte version, a one-byte command, a two-byte length, and a four-byte stream ID. The payload follows that header. Protocol versions 1 and 2 are listed. Version 2 adds cmdUPD, which carries consumed bytes and a window value. The README also documents cmdSYN, cmdFIN, cmdPSH, and cmdNOP for stream setup, closure, push data, and keepalive traffic.

Smux assigns odd stream IDs to clients beginning at 1 and even stream IDs to servers beginning at 0. The built-in shaper provides fair queue traffic shaping, while protocol version 2 supports a per-stream sliding window for congestion control. The README describes token-bucket-controlled receiving, intended to smooth the receive-side bandwidth pattern. These features address a different problem from ordinary buffering: they distribute progress among streams and limit how much data can be outstanding.

Allocation strategy and memory boundaries

Memory control is a central design choice. The session has a shared receive buffer whose maximum size is configured by MaxReceiveBuffer, rather than allowing each stream to reserve memory independently. MaxStreamBuffer sets a per-stream limit, and MaxFrameSize controls the maximum frame size. Together these settings give an application explicit ceilings for the session and its individual streams.

The frame allocator in alloc.go uses a slab-style design for frames up to 64 KB. According to the README, 17 sync.Pool buckets cache power-of-two slice capacities. A De Bruijn-based msb lookup selects the smallest suitable bucket in constant time. The allocator deliberately reuses buffers without zeroing them because a new frame overwrites the payload that it needs. The repository documentation presents this as a way to reduce GC pressure and repeated runtime clearing.

That reuse does not make the shared buffer unlimited. The documented design predicts bounded fragmentation of less than 50 percent for allocator requests, but the actual memory profile still depends on configured limits, frame sizes, stream counts, and application behavior. Smux therefore trades an opaque, per-connection implementation for configurable global and local bounds. Teams should treat those configuration values as part of the transport contract rather than as harmless tuning defaults.

Installing smux and establishing a first session

The repository does not provide an installation command in the README. It identifies the Go module as github.com/xtaci/smux and points readers to the associated Godoc documentation for complete API details. A consumer project would need to obtain that module through its normal Go dependency workflow, but no exact version-selection or installation command is documented in the README, so this article does not invent one.

The first use is to establish a TCP connection, create a client or server session, and then open or accept a stream. The README shows the following client sequence. The code assumes that conn already exists, and the ellipsis in net.Dial(...) is retained because the README does not specify a network, address, or timeout.

go
conn, err := net.Dial(...)
if err != nil {
    panic(err)
}
session, err := smux.Client(conn, nil)
if err != nil {
    panic(err)
}
stream, err := session.OpenStream()
if err != nil {
    panic(err)
}
stream.Write([]byte("ping"))
stream.Close()
session.Close()

The client writes a short payload to one logical stream, closes that stream, and then closes the session. The server side receives the TCP connection through listener.Accept(), wraps it with smux.Server(), and waits for a stream with AcceptStream(). The README shows the receiving side reading four bytes:

go
conn, err := listener.Accept()
if err != nil {
    panic(err)
}
session, err := smux.Server(conn, nil)
if err != nil {
    panic(err)
}
stream, err := session.AcceptStream()
if err != nil {
    panic(err)
}
buf := make([]byte, 4)
stream.Read(buf)
stream.Close()
session.Close()

Both peers must use compatible smux framing. The README does not document authentication, encryption, retry behavior, or rollback procedures.

Configuration choices that change transport behavior

smux.Config exposes six relevant session parameters. Version selects protocol version 1 or 2. KeepAliveInterval controls how often NOP frames are sent, while KeepAliveTimeout controls how long the session can remain without received data before it is closed. These settings are relevant for transports whose liveness rules differ from ordinary TCP assumptions, but the README gives no recommended values.

MaxFrameSize, MaxReceiveBuffer, and MaxStreamBuffer define the main buffering envelope. A larger frame size can reduce header overhead, while a larger shared receive buffer allows more data to remain queued across the session. A per-stream buffer limit can prevent one logical stream from consuming the entire shared allowance, although the fair queue shaper also influences distribution. The README does not state whether changing these values is safe on a live session or how a version mismatch is negotiated, so configuration compatibility should be verified before deployment.

Protocol version 2 is particularly relevant when an application needs the documented per-stream sliding window. Version 1 remains listed for compatibility, but it does not support cmdUPD. The choice is therefore not merely a build flag. It changes the available congestion-control mechanism and should be treated as an endpoint compatibility decision.

Where smux is the wrong layer

Smux is the wrong choice when the application only needs one reliable byte stream. In that case, adding stream IDs, SYN and FIN handling, keepalive frames, shared receive buffering, and traffic shaping creates work without a corresponding need. It is also unsuitable as a substitute for a transport that lacks reliable, ordered delivery, because the README explicitly places those guarantees in the underlying connection.

There is another practical limitation: a stream is not an independent network path. The client and server share one underlying connection, so congestion, disconnects, and transport-level stalls apply to all streams in that session. The fair queue shaper and version 2 window can regulate behavior, but the README does not claim that smux creates separate bandwidth reservations or independent recovery.

The library also does not document rollback, stream migration, encryption, authentication, or a high-level message protocol. Those omissions matter when evaluating an adoption beyond a controlled internal transport. The project provides a framing and session layer, not a complete secure network protocol.

Smux versus hashicorp/yamux and direct TCP

The README names hashicorp/yamux as a reference, but it does not document a detailed feature comparison. The concrete difference visible in this repository is that smux exposes token-bucket-controlled receiving, a session-wide receive buffer, an 8-byte frame header, fair queue traffic shaping, and a version 2 per-stream sliding window. A team evaluating yamux should compare its own buffering, congestion-control, and protocol behavior rather than infer equivalence from the shared multiplexing category.

Direct TCP is the other meaningful alternative. TCP supplies one ordered byte stream and leaves stream framing, application-level message boundaries, and connection composition to the application. Smux moves those concerns into a reusable Go library, but adds a session layer and its own configuration model. The choice is between composing a transport stack around multiple logical connections and accepting smux's framing, flow-control, and memory policy.

The project is licensed under MIT. That identifier permits use and modification under the license's conditions, but this review does not interpret the license or provide legal advice. The README points to Godoc for the complete API, while the repository layout places frame handling in frame.go, session management in session.go, stream behavior in stream.go, and shaping logic in shaper.go.

Editorial conclusion

Adopt smux when an application needs several logical net.Conn streams over a reliable, ordered transport and can accept smux-specific framing and flow-control behavior. Do not choose it when a raw TCP connection is sufficient or when the underlying transport does not provide the required reliability and ordering. Before adoption, verify protocol version 2 support, receive-buffer sizing, stream-window behavior, keepalive settings, and compatibility with both peers. The project is under the MIT License, and its latest documented release is v1.5.53 from 2026-01-26, while the last repository push was on 2026-05-15.

Frequently asked questions

What does xtaci/smux do in Go?

xtaci/smux is a Go library that multiplexes multiple logical streams over an existing reliable and ordered connection such as TCP or KCP. Its streams implement net.Conn.

How do I install and configure xtaci/smux?

The README does not give an installation command. It identifies the module as github.com/xtaci/smux and documents smux.Config fields including Version, KeepAliveInterval, KeepAliveTimeout, MaxFrameSize, MaxReceiveBuffer, and MaxStreamBuffer.

Does xtaci/smux support congestion control and shared buffering?

Yes. The README documents a per-stream sliding window for protocol version 2, fair queue traffic shaping, token-bucket-controlled receiving, and a session-wide receive buffer shared among streams.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. xtaci/smux on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/xtaci-smux.svg)](https://hysenlabs.com/projects/xtaci-smux)