# skywind3000/kcp: an ARQ protocol in two C files that trades bandwidth for latency

> KCP is a pure-algorithm reliable transport that runs over whatever datagram layer you already have. It costs 10 to 20 percent more bandwidth than TCP to cut average latency by 30 to 40 percent, and it never makes a system call of its own.

**skywind3000/kcp** — :zap: KCP - A Fast and Reliable ARQ Protocol

- Repository: https://github.com/skywind3000/kcp
- Stars: 16,926 · Forks: 2,632
- Language: C
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/skywind3000-kcp

## The problem KCP solves: TCP optimizes for volume, not for arrival time

TCP is designed around how many kilobytes per second a link can carry. The README makes the distinction explicit: TCP is a wide, slow canal, while KCP is a narrow stream with fast water. Every mechanism in TCP that improves bandwidth utilization, delayed ACKs, retransmitting everything after a lost segment, doubling RTO on each consecutive loss, works against a small packet that needs to arrive now.

KCP is for the case where you already have a datagram channel (usually UDP) and you need reliability on top of it without inheriting TCP's timing behavior. The README names the audience directly: someone who has written a P2P layer or a UDP-based protocol and lacks a complete ARQ implementation. The pitch is that copying ikcp.h and ikcp.c into an existing project and writing a couple of lines gets you there.

This is not a general replacement for TCP. It is a component. It has no opinion about how packets reach the other side, and it is not a socket library.

## How the ARQ loop actually works: callbacks in, callbacks out, an external clock

The repository is two source files, ikcp.h and ikcp.c, plus test.cpp, test.h, protocol.txt and a CMakeLists.txt. There is no networking code in the library itself. The README states plainly that KCP does not handle the underlying protocol's send and receive, that the user defines how lower-layer packets are sent and supplies that as a callback, and that even the clock is passed in from outside. It makes no system call internally.

The data flow has four entry points. ikcp_create(conv, user) allocates a session, where conv is a session number that both ends must agree on, the same idea as a TCP connection identifier. You assign kcp->output, a function KCP calls whenever it has bytes to put on the wire. You call ikcp_update(kcp, millisec) on a timer to advance protocol state. Incoming datagrams go to ikcp_input(kcp, received_udp_packet, received_udp_size). Application data goes out through ikcp_send and comes back through ikcp_recv.

The acceleration comes from four specific choices described in the README. On timeout, TCP doubles RTO (three losses means RTO times eight); KCP's fast mode multiplies by 1.5 instead. On loss, TCP retransmits everything from the lost segment forward; KCP retransmits selectively. KCP infers loss from skipped ACKs: if the sender transmits 1 through 5 and receives ACKs for 1, 3, 4 and 5, the ACK for 3 means packet 2 was skipped once, the ACK for 4 means twice, and at that point 2 is treated as lost and retransmitted without waiting for a timeout. Finally, KCP lets you turn off delayed ACKs, which TCP sends to fill bandwidth and which inflate the measured RTT.

The fifth choice is in the wire format. ARQ responses come in two flavors: UNA, meaning everything below this number has arrived, and ACK, meaning this specific packet arrived. UNA alone forces full retransmission; ACK alone makes loss accounting expensive. KCP carries UNA information in every packet except standalone ACK packets, so both signals travel together.

## Installing kcp and getting a first session running

There is no package to install for the library itself. You take the two files. The README does document one managed route, through Microsoft's vcpkg, which keeps a kcp port that Microsoft team members and community contributors maintain:

```bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install kcp
```

The README notes that if the vcpkg port falls behind, the fix is an issue or a pull request on the vcpkg repository, not here. For everything else, copy ikcp.c and ikcp.h into your tree. The repository also ships a CMakeLists.txt, so a CMake-based project has a build path without vcpkg.

Once the files are in place, four steps wire up a session. The README gives each one as a snippet. First create the object:

```cpp
ikcpcb *kcp = ikcp_create(conv, user);
```

Then hand KCP the function it should call to emit bytes:

```cpp
int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
{
  ....
}
kcp->output = udp_output;
```

Drive the state machine from a timer. The README suggests calling ikcp_update every 10ms, or using ikcp_check to find out when the next update is due instead of calling it constantly:

```cpp
ikcp_update(kcp, millisec);
```

Feed received datagrams in from your socket loop:

```cpp
ikcp_input(kcp, received_udp_packet, received_udp_size);
```

After that, ikcp_send writes to the peer and ikcp_recv reads what arrived. What you should see is your own UDP socket carrying KCP-formatted packets, and no new sockets opened by the library. If nothing is transmitted, the usual cause is that ikcp_update is not being called or the output callback is not set.

Out of the box the protocol is a plain ARQ. The acceleration switches are off until you set them:

```cpp
ikcp_nodelay(kcp, 1, 10, 2, 1);
```

The four arguments are nodelay (1 enables it), interval in milliseconds (10 or 20 are the README's examples), resend (2 means a packet skipped by two ACK crossings is retransmitted immediately), and nc (1 disables flow control). The README lists ikcp_nodelay(kcp, 0, 40, 0, 0) as normal mode and ikcp_nodelay(kcp, 1, 10, 2, 1) as the fast mode. Window size defaults to 32 and is set with ikcp_wndsize(kcp, sndwnd, rcvwnd), counted in packets rather than bytes, unlike TCP's SND_BUF and RCV_BUF. MTU defaults to 1400 bytes and is changed with ikcp_setmtu; the README points out that a pure-algorithm protocol does not probe MTU, so discovery is your problem. Minimum RTO defaults to 100ms, or 30ms in fast mode, and is changed by assigning kcp->rx_minrto = 10 directly.

## Where KCP is the wrong tool

The bandwidth cost is real and stated up front: 10 to 20 percent more than TCP. On a metered or congested link that is a genuine trade, not a rounding error. If your workload is bulk transfer where total time matters more than per-packet latency, TCP already does that job and does it without you writing a session layer.

The non-retreat flow control option is the sharpest edge. Normally KCP uses the same fair-backoff rule as TCP, with the send window determined by send buffer size, the receiver's remaining buffer, loss backoff, and slow start. Setting nc to 1 skips the last two and uses only the first two. The README is candid about the price: reduced fairness and reduced bandwidth utilization, bought in exchange for smooth transmission even while a BitTorrent client is saturating the link. That is a deliberate choice to be a bad network citizen in exchange for latency, and it will not endear you to anyone sharing the pipe.

Two operational gaps follow from the design. Because the library makes no system calls, there is no thread of its own: you must call ikcp_update on a schedule, and if your output callback blocks, you are blocking whatever thread is driving the protocol. And because KCP does not probe MTU, a path with a smaller MTU than your configured value will fragment or drop, and the library will not discover it.

The README also flags a version split. KCP is now at V2, which adds a pluggable flow control mechanism so the algorithm can be swapped without changing the protocol. V1 is described as sufficiently stable and lives on the v1 branch. If you are pinning a dependency, that is the branch decision you have to make before anything else.

## Alternatives, and how they differ in approach

The most direct comparison is kcptun, which the README lists first among open source cases. kcptun is a remote port forwarder built on kcp-go, the Go implementation of KCP. The difference is one of scope: KCP is a library you embed and drive yourself, while kcptun is a finished tunnel binary that pairs with ssh -D. If you want a tunnel, kcptun is the shorter path. If you need reliability inside your own protocol, kcptun is the wrong layer.

The language ports matter for the same reason. kcp-go, kcp-java, kcp-netty, java-Kcp, kcp.kt, kcp-rs, kcp-rust, tokio-kcp, kcp-cpp, kcp-perl and the several C# ports listed in the README all reimplement the same protocol in a runtime that manages memory and concurrency for you. Choosing one of those over the C original usually means you want the session management and threading that the C library deliberately omits. The README points to asio-kcp as a complete UDP network library with connection state management, session control and KCP scheduling, which is exactly the layer the two C files leave out.

Against QUIC, the split is architectural. QUIC is a full transport with its own handshake, encryption and multiplexing. KCP is an ARQ algorithm with no handshake and no encryption; the README's documentation index lists network encryption as a separate wiki topic, which tells you it is not in the library. If you need TLS-grade security as a baseline rather than an add-on, KCP is the wrong starting point.

## Maintenance, licence, and what upgrading costs

The repository is not archived and the last push was on 2026-06-23. Three releases landed in the months before that: 2.1.1 on 2026-05-15, 2.0.0 on 2026-05-14, and 1.7.1 on 2026-05-01. The 2.0.0 release is the one that introduced the pluggable flow control mechanism described in the README, so anyone on 1.x is looking at a protocol-level change, not a patch, when they move to V2.

The upgrade path is unusual in a good way: because the library is two files, upgrading means replacing ikcp.c and ikcp.h. There is no build system to reconcile beyond the CMakeLists.txt, no transitive dependency tree, and no ABI surface beyond the functions you call. The cost is that you own the diff. The README does not document a migration guide from v1 to v2, and it does not document rollback. The only stated guidance is that v1 remains on the v1 branch for projects that do not want the flow control change.

The licence is MIT, per the repository's LICENSE file and the badge in the README. MIT permits use in closed-source products and requires preserving the copyright notice and licence text. That is the extent of what the README and LICENSE support; whether MIT fits a given product's compliance process is a question for your own legal review, not something this repository answers.

## Conclusion

Adopt KCP when you already own a UDP path and the thing you are optimizing is per-packet delivery time, not throughput. Do not adopt it if you want a socket API, congestion fairness against TCP, or encryption out of the box: the README points at wiki pages for those, and none of them are in ikcp.c. Before committing, verify two things in your own build: that your output callback never blocks the thread calling ikcp_update, and that you have decided who owns the clock, because KCP will not read one for you.

## FAQ

### What is the KCP protocol?

KCP is a fast, reliable ARQ protocol implemented purely as an algorithm. It does not handle the underlying transport such as UDP, does not make any system calls, and takes its clock from the caller. The README describes it as trading 10 to 20 percent more bandwidth than TCP for 30 to 40 percent lower average latency.

### How do I install KCP?

There is no package for the library itself; you copy ikcp.c and ikcp.h into your project. The README also documents installing it through the vcpkg library manager with ./vcpkg install kcp, and the repository ships a CMakeLists.txt for CMake-based builds.

### Why does KCP need me to provide a clock and an output callback?

KCP is a pure algorithm that does not perform any system call internally, so it cannot send packets or read time on its own. You assign kcp->output to the function that transmits bytes, call ikcp_update with a millisecond value on a timer, and pass received datagrams to ikcp_input.

### Does KCP provide encryption or MTU discovery?

Neither is in the library. The README states that a pure-algorithm protocol does not probe MTU and that the default is 1400 bytes, settable with ikcp_setmtu. Encryption is listed as a separate topic in the wiki documentation index rather than as a feature of ikcp.c.

### What is the difference between KCP v1 and v2?

V2 adds a pluggable flow control mechanism so the flow control algorithm can be replaced without changing the protocol. The README says v1 is sufficiently stable and keeps it on the v1 branch.

## Sources

- [Issues](https://github.com/skywind3000/kcp/issues)
- [License: MIT](https://github.com/skywind3000/kcp/blob/master/LICENSE)
- [README](https://github.com/skywind3000/kcp/blob/master/README.md)
- [Releases](https://github.com/skywind3000/kcp/releases)
- [skywind3000/kcp on GitHub](https://github.com/skywind3000/kcp)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/skywind3000-kcp
