kcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bf16edd731053438f070b9bd3c31917fc51a6c0c33b38f650fbc81b5fc2f8db
4
+ data.tar.gz: 7b04f35a6eca243cde48a7e8c7b1fa1818a93c50a5a9fb39df1f39ed51299f3c
5
+ SHA512:
6
+ metadata.gz: 16e4a8fafb2265f223b54fd3ad7d89b9d15ac4fbbc33793606164e6ec1b04f478f5db203b1b703a244067abfe380422d35c0d7ece1ff04d4c269f44be47e6880
7
+ data.tar.gz: fae9a45f110c3e0cbc9ccd4f364bcf71595c4e626bfc00921de7915343030d2ab830759a8d0871b3e1a6e429103d3f0755e679cdcd21cd1e7d7ab124eae3138c
data/README.en.md ADDED
@@ -0,0 +1,235 @@
1
+ # kcp
2
+
3
+ [简体中文](README.md) | English
4
+
5
+ Ruby bindings for [KCP](https://github.com/skywind3000/kcp) (A Fast and Reliable ARQ Protocol),
6
+ provided as a **native C extension**. KCP is a fast, reliable transport protocol (ARQ) written in C,
7
+ offering lower latency and better real-time behaviour than TCP over lossy / weak networks.
8
+
9
+ ## Features
10
+
11
+ - **Native C extension**: compiles the KCP C source (skywind3000/kcp) directly, with C-level performance
12
+ - **Cross-platform**: built via `mkmf` and the Ruby toolchain — **x86_64 / arm64**, **macOS / Linux / Windows**
13
+ - **Byte-stream mode** (`stream = 1`): data delivered as an ordered byte stream, no message framing
14
+ - **Pull-model API**: the engine does not touch sockets; you send/receive UDP datagrams yourself via `output`/`input`
15
+ - Zero runtime dependencies (Ruby >= 3.0 only)
16
+
17
+ ## Installation
18
+
19
+ ```ruby
20
+ # Gemfile
21
+ gem 'kcp'
22
+ ```
23
+
24
+ ```bash
25
+ gem install kcp
26
+ ```
27
+
28
+ The C sources under `ext/kcp` (`ikcp.c` + `kcp_native.c`) are compiled automatically at install time;
29
+ Ruby's `mkmf` picks the right compiler and architecture (MSVC/MinGW on Windows, clang/gcc on macOS/Linux,
30
+ arm/x64 matched automatically).
31
+
32
+ ## Quick start
33
+
34
+ The KCP engine only guarantees "reliably deliver a byte stream to the peer". It does **not** handle
35
+ sockets, addressing, or encryption. Create one engine per endpoint and feed each other UDP datagrams:
36
+
37
+ ```ruby
38
+ require 'kcp'
39
+
40
+ CONV = 0x1122_3344 # session id, must match on both ends
41
+
42
+ # ---- sender ----
43
+ engine = Kcp::Engine.new(CONV)
44
+
45
+ engine.send("hello kcp") # queue data
46
+ engine.flush # flush immediately
47
+
48
+ # pull encoded KCP packets and hand them to UDP sendto
49
+ while (pkt = engine.output)
50
+ udp_socket.send(pkt, 0, peer_host, peer_port)
51
+ end
52
+
53
+ # ---- receiver (after receiving a UDP datagram) ----
54
+ engine.input(recv_datagram) # feed the received packet
55
+
56
+ # pull the ordered, delivered byte stream
57
+ data = engine.recv # => "hello kcp" (nil when no data)
58
+
59
+ # drive the clock periodically (retransmit / window update / keepalive)
60
+ engine.update(Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000)
61
+ ```
62
+
63
+ ## API
64
+
65
+ ### `Kcp::Engine.new(conv)`
66
+
67
+ Create an engine. `conv` is a 32-bit session id (`uint32`); **both ends of a connection must use the
68
+ same `conv`**. Use a different `conv` per concurrent connection.
69
+
70
+ ```ruby
71
+ engine = Kcp::Engine.new(0x1234)
72
+ ```
73
+
74
+ ### `send(data)`
75
+
76
+ Queue data for reliable delivery. Returns the number of bytes queued, or a negative value on error.
77
+ **Data is not sent immediately** — call `flush` (immediate) or `update` (interval) to produce packets.
78
+
79
+ ```ruby
80
+ engine.send("some bytes")
81
+ ```
82
+
83
+ ### `flush`
84
+
85
+ Flush the send queue into encoded packets immediately (not throttled by `update`'s interval).
86
+ Use `output` afterwards to pull them.
87
+
88
+ ```ruby
89
+ engine.send(data)
90
+ engine.flush
91
+ pkt = engine.output
92
+ ```
93
+
94
+ ### `output`
95
+
96
+ Pull pending encoded packets (a concatenated byte segment, ready for UDP `send`). Returns `nil` when
97
+ there is nothing to send. **The pulled bytes must be sent verbatim to the peer**, which feeds them
98
+ back via `input`.
99
+
100
+ ```ruby
101
+ while (pkt = engine.output)
102
+ socket.send(pkt, 0, host, port)
103
+ end
104
+ ```
105
+
106
+ ### `input(data)`
107
+
108
+ Feed one received encoded packet (the contents of a low-level UDP datagram). KCP parses it and handles
109
+ ACKs / retransmits / data; afterwards use `recv` to read data and `output` to pull packets to send back
110
+ (e.g. ACKs).
111
+
112
+ ```ruby
113
+ socket.recvfrom(65_536) do |data, addr|
114
+ engine.input(data)
115
+ end
116
+ ```
117
+
118
+ ### `recv(maxlen = 65536)`
119
+
120
+ Pull up to `maxlen` bytes of the peer's delivered, **ordered** byte stream. Returns `nil` when there is
121
+ no data.
122
+
123
+ ```ruby
124
+ if (chunk = engine.recv)
125
+ # handle the received data
126
+ end
127
+ ```
128
+
129
+ ### `update(now_ms)`
130
+
131
+ Drive the KCP clock: interval-based retransmit, ACK, window probing. `now_ms` is a **monotonic
132
+ millisecond timestamp** (use `Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000`).
133
+
134
+ Call it periodically in the main loop, e.g. every 10–100ms:
135
+
136
+ ```ruby
137
+ loop do
138
+ engine.update((Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i)
139
+ sleep 0.01
140
+ end
141
+ ```
142
+
143
+ ### `close`
144
+
145
+ Drop the reference to the C object (the underlying C memory is reclaimed by the Ruby GC via
146
+ `TypedData_Wrap_Struct`).
147
+
148
+ ```ruby
149
+ engine.close
150
+ ```
151
+
152
+ ## About `update` and `now_ms`
153
+
154
+ KCP has **no internal timer** and never reads the system clock itself. You must feed it the current
155
+ time (`now_ms`) periodically so it can drive all time-related logic:
156
+
157
+ - **Timeout retransmit**: each sent segment carries a `resendts` (next resend time); it is only resent
158
+ once `current` advances past it
159
+ - **RTT measurement**: uses `current - segment.ts` to compute round-trip time, and from it the RTO
160
+ - **Window probing**: sends WINS probes periodically when the peer's window is zero
161
+ - **Dead-link detection**: declares the link dead after a timeout with no response
162
+
163
+ `now_ms` must be a **monotonic millisecond timestamp** (`Process.clock_gettime(Process::CLOCK_MONOTONIC)`),
164
+ because the monotonic clock only moves forward and is unaffected by system-time jumps (NTP sync, manual
165
+ changes). It is a 32-bit millisecond value that wraps every ~49 days; KCP handles the wraparound with a
166
+ signed difference internally, which is expected.
167
+
168
+ ### What happens if you don't `update`?
169
+
170
+ 1. The first line of `ikcp_flush` is `if (kcp->updated == 0) return;` — and `updated` is only set by
171
+ `update`, so without `update` nothing can even be sent (this gem's `flush` calls `update` once as a
172
+ fallback, so `send + flush` alone still works).
173
+ 2. Even if you bypass that, **retransmit relies on `current` advancing**: after a packet is lost, if you
174
+ don't `update`, `resendts` is never reached, so that single loss stalls the connection forever; ACKs
175
+ are also not flushed on interval, the peer's window never advances, and both directions freeze.
176
+
177
+ Bottom line: **`update` is KCP's heartbeat**. `send + flush` is "send immediately" (low latency);
178
+ `update` is "periodic drive" (retransmit / ACK / window / keepalive). Call it every 10–100ms — without
179
+ it, a single packet loss kills the connection.
180
+
181
+ ## Full example (UDP echo)
182
+
183
+ The `examples/` directory ships runnable server and client:
184
+
185
+ ```bash
186
+ ruby -I lib examples/server.rb 9000 # terminal 1: start the server
187
+ ruby -I lib examples/client.rb 127.0.0.1 9000 # terminal 2: type a line to echo, /quit to exit
188
+ ```
189
+
190
+ The full code follows:
191
+
192
+ ```ruby
193
+ require 'socket'
194
+ require 'kcp'
195
+
196
+ CONV = 0x1234
197
+
198
+ def now_ms
199
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i
200
+ end
201
+
202
+ # ---- server ----
203
+ server = UDPSocket.new
204
+ server.bind('0.0.0.0', 9000)
205
+ engine = Kcp::Engine.new(CONV)
206
+
207
+ loop do
208
+ data, addr = server.recvfrom(65_536)
209
+ engine.input(data)
210
+
211
+ while (chunk = engine.recv)
212
+ engine.send(chunk) # echo
213
+ end
214
+ engine.flush
215
+ while (pkt = engine.output)
216
+ server.send(pkt, 0, addr[3], addr[1])
217
+ end
218
+ engine.update(now_ms)
219
+ end
220
+ ```
221
+
222
+ ## Notes
223
+
224
+ 1. **KCP only does reliable transport**; socket I/O, peer-address management, encryption and session
225
+ keepalive are up to the caller (the `mnet` gem in this repo builds a roaming transport layer that
226
+ survives IP changes on top of it).
227
+ 2. `conv` must match on both ends; use a different `conv` per concurrent connection.
228
+ 3. `flush` is immediate (low latency); `update` is interval-throttled (saves CPU). Use both for
229
+ interactive workloads.
230
+ 4. KCP uses a 32-bit millisecond timestamp that wraps every ~49 days; this is expected (the protocol
231
+ handles the wraparound).
232
+
233
+ ## License
234
+
235
+ MIT. KCP source copyright skywind3000.
data/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # kcp
2
+
3
+ [English](README.en.md) | 简体中文
4
+
5
+ [KCP](https://github.com/skywind3000/kcp)(A Fast and Reliable ARQ Protocol)的 Ruby 绑定,
6
+ 以**原生 C 扩展**形式提供。KCP 是一个用 C 实现的快速可靠传输协议(ARQ),
7
+ 相比 TCP 在丢包/弱网场景下有更低的延迟和更好的实时性。
8
+
9
+ ## 特性
10
+
11
+ - **原生 C 扩展**:直接编译 skywind3000/kcp 的 C 源码,性能与 C 一致
12
+ - **跨平台**:通过 `mkmf` + Ruby 工具链构建,支持 **x86_64 / arm64**,**macOS / Linux / Windows**
13
+ - **字节流模式**(`stream = 1`):数据按有序字节流交付,无消息边界
14
+ - **拉取式接口**:引擎不碰 socket,你通过 `output`/`input` 自己收发 UDP 数据报
15
+ - 零运行时依赖(除 Ruby >= 3.0)
16
+
17
+ ## 安装
18
+
19
+ ```ruby
20
+ # Gemfile
21
+ gem 'kcp'
22
+ ```
23
+
24
+ ```bash
25
+ gem install kcp
26
+ ```
27
+
28
+ 构建时会自动编译 `ext/kcp` 下的 C 源码(`ikcp.c` + `kcp_native.c`),
29
+ 由 Ruby 的 `mkmf` 负责选择对应平台的编译器与架构(Windows 用 MSVC/MinGW,macOS/Linux 用 clang/gcc,arm/x64 自动匹配)。
30
+
31
+ ## 快速开始
32
+
33
+ KCP 引擎只管「把字节流可靠送达对端」,**不处理 socket、地址、加密**。
34
+ 两端各创建一个引擎,通过 UDP 数据报互相喂包即可:
35
+
36
+ ```ruby
37
+ require 'kcp'
38
+
39
+ CONV = 0x1122_3344 # 会话标识,两端必须一致
40
+
41
+ # ---- 发送端 ----
42
+ engine = Kcp::Engine.new(CONV)
43
+
44
+ engine.send("hello kcp") # 数据进入发送队列
45
+ engine.flush # 立即刷出
46
+
47
+ # 从引擎取出编码后的 KCP 包,交给 UDP sendto
48
+ while (pkt = engine.output)
49
+ udp_socket.send(pkt, 0, peer_host, peer_port)
50
+ end
51
+
52
+ # ---- 接收端(收到 UDP 数据报后)----
53
+ engine.input(recv_datagram) # 喂入收到的包
54
+
55
+ # 从引擎取出按序送达的字节流
56
+ data = engine.recv # => "hello kcp"(没有数据时返回 nil)
57
+
58
+ # 周期性驱动(重传 / 窗口更新 / 心跳)
59
+ engine.update(Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000)
60
+ ```
61
+
62
+ ## 接口说明
63
+
64
+ ### `Kcp::Engine.new(conv)`
65
+
66
+ 创建引擎。`conv` 是 32 位会话标识(`uint32`),**同一条连接的两端必须使用相同的 `conv`**,
67
+ 用于区分不同的会话。多个并发连接时应为每个连接分配不同的 `conv`。
68
+
69
+ ```ruby
70
+ engine = Kcp::Engine.new(0x1234)
71
+ ```
72
+
73
+ ### `send(data)`
74
+
75
+ 把数据排入发送队列,返回排入的字节数,出错返回负数。**数据不会立即发出**,
76
+ 需要调用 `flush`(立即)或 `update`(按间隔)才会生成编码包。
77
+
78
+ ```ruby
79
+ engine.send("some bytes")
80
+ ```
81
+
82
+ ### `flush`
83
+
84
+ 立即把发送队列里的数据刷成编码包(不受 `update` 的间隔节流)。刷完后用 `output` 取出。
85
+
86
+ ```ruby
87
+ engine.send(data)
88
+ engine.flush
89
+ pkt = engine.output
90
+ ```
91
+
92
+ ### `output`
93
+
94
+ 取出待发送的编码包(可能是一段拼接好的字节,直接交给 UDP `send`)。没有待发送数据时返回 `nil`。
95
+ **取出的字节必须原样发给对端**,对端收到后原样 `input`。
96
+
97
+ ```ruby
98
+ while (pkt = engine.output)
99
+ socket.send(pkt, 0, host, port)
100
+ end
101
+ ```
102
+
103
+ ### `input(data)`
104
+
105
+ 喂入一个收到的编码包(低层 UDP 数据报的内容)。KCP 会解析、处理 ACK/重传/数据,
106
+ 随后可用 `recv` 取数据、用 `output` 取要回发的包(例如 ACK)。
107
+
108
+ ```ruby
109
+ socket.recvfrom(65_536) do |data, addr|
110
+ engine.input(data)
111
+ end
112
+ ```
113
+
114
+ ### `recv(maxlen = 65536)`
115
+
116
+ 取出对端已送达的、**按序**的字节流(最多 `maxlen` 字节)。没有数据时返回 `nil`。
117
+
118
+ ```ruby
119
+ if (chunk = engine.recv)
120
+ # 处理收到的数据
121
+ end
122
+ ```
123
+
124
+ ### `update(now_ms)`
125
+
126
+ 驱动 KCP 时钟:触发按间隔的重传、ACK、窗口探测等。`now_ms` 是**单调毫秒时间戳**
127
+ (建议用 `Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000`)。
128
+
129
+ 应在主循环里周期性调用,例如每 10~100ms 一次:
130
+
131
+ ```ruby
132
+ loop do
133
+ engine.update((Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i)
134
+ sleep 0.01
135
+ end
136
+ ```
137
+
138
+ ### `close`
139
+
140
+ 释放对 C 对象的引用(底层 C 内存由 Ruby GC 通过 `TypedData_Wrap_Struct` 自动回收)。
141
+
142
+ ```ruby
143
+ engine.close
144
+ ```
145
+
146
+ ## 关于 `update` 与 `now_ms`
147
+
148
+ KCP **内部没有计时器**,不会自己读系统时间。你必须周期性地喂给它当前时间(`now_ms`),
149
+ 它才能驱动所有和时间相关的逻辑:
150
+
151
+ - **超时重传**:每个已发出的段带一个 `resendts`(下次重传时刻),`current` 推进到它之后才会重传
152
+ - **RTT 测量**:用 `current - 段.ts` 算往返时间,进而算出 RTO
153
+ - **窗口探测**:对端窗口为 0 时,定时发送 WINS 探测
154
+ - **死链检测**:超时无响应则判死
155
+
156
+ `now_ms` 必须是**单调毫秒时间戳**(`Process.clock_gettime(Process::CLOCK_MONOTONIC)`),
157
+ 因为单调时钟只前进、不受系统时间跳变(NTP 校时、手动改时间)干扰。它是 32 位毫秒,约 49 天回绕一次,
158
+ KCP 内部用有符号差值处理回绕,属正常现象。
159
+
160
+ ### 不 `update` 会怎样
161
+
162
+ 1. `ikcp_flush` 第一行是 `if (kcp->updated == 0) return;` —— 而 `updated` 只有 `update` 才置位,
163
+ 所以不 update 时,连包都发不出去(本 gem 的 `flush` 内部会先兜底调一次 update,故单独 `send + flush` 仍可用)。
164
+ 2. 即便绕过第 1 点,**重传依赖 `current` 推进**:丢了一个包后若不 update,`resendts` 永远到不了,
165
+ 这一个丢包就会永久卡死;ACK 也不会按间隔刷出,对端窗口不前进,双向一起僵住。
166
+
167
+ 结论:**`update` 就是 KCP 的心跳**。`send + flush` 是「立即发」(低延迟),`update` 是「周期性驱动」
168
+ (重传 / ACK / 窗口 / 保活)。每 10~100ms 调一次;缺了它,只要发生一次丢包连接就死。
169
+
170
+ ## 完整示例(UDP 回显)
171
+
172
+ 仓库内 `examples/` 下有可直接运行的服务端和客户端:
173
+
174
+ ```bash
175
+ ruby -I lib examples/server.rb 9000 # 终端 1:起服务端
176
+ ruby -I lib examples/client.rb 127.0.0.1 9000 # 终端 2:连上后输入即回显,/quit 退出
177
+ ```
178
+
179
+ 下面是对应的完整代码:
180
+
181
+ ```ruby
182
+ require 'socket'
183
+ require 'kcp'
184
+
185
+ CONV = 0x1234
186
+
187
+ def now_ms
188
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i
189
+ end
190
+
191
+ # ---- 服务端 ----
192
+ server = UDPSocket.new
193
+ server.bind('0.0.0.0', 9000)
194
+ engine = Kcp::Engine.new(CONV)
195
+
196
+ loop do
197
+ data, addr = server.recvfrom(65_536)
198
+ engine.input(data)
199
+
200
+ while (chunk = engine.recv)
201
+ engine.send(chunk) # 回显
202
+ end
203
+ engine.flush
204
+ while (pkt = engine.output)
205
+ server.send(pkt, 0, addr[3], addr[1])
206
+ end
207
+ engine.update(now_ms)
208
+ end
209
+ ```
210
+
211
+ ## 注意事项
212
+
213
+ 1. **KCP 只管可靠传输**,网络 I/O、对端地址管理、加密、会话保活都由上层负责
214
+ (本仓库的 `mnet` gem 在其上实现了「移动 IP 不中断」的漫游传输层)。
215
+ 2. `conv` 两端必须一致;并发连接请用不同 `conv`(或在上层协议里用会话 ID 复用)。
216
+ 3. `flush` 是立即刷出(低延迟);`update` 是按间隔节流(省 CPU)。高频交互场景两者配合使用。
217
+ 4. KCP 使用 32 位毫秒时间戳,约 49 天回绕一次,属正常现象(协议内部处理了回绕)。
218
+
219
+ ## 许可
220
+
221
+ MIT。KCP 源码版权归 skywind3000。
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf'
4
+
5
+ # KCP + our shim. mkmf handles cross-compilation for arm/x86_64 and Windows
6
+ # (MinGW/MSVC) via the Ruby toolchain (rbconfig).
7
+ $srcs = %w[ikcp.c kcp_native.c]
8
+
9
+ create_makefile('kcp_native')