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.
data/ext/kcp/ikcp.h ADDED
@@ -0,0 +1,452 @@
1
+ //=====================================================================
2
+ //
3
+ // KCP - A Better ARQ Protocol Implementation
4
+ // skywind3000 (at) gmail.com, 2010-2011
5
+ //
6
+ // Features:
7
+ // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
8
+ // + Maximum RTT reduce three times vs tcp.
9
+ // + Lightweight, distributed as a single source file.
10
+ //
11
+ //=====================================================================
12
+ #ifndef _IKCP_H_
13
+ #define _IKCP_H_
14
+
15
+ #include <stddef.h>
16
+ #include <stdlib.h>
17
+ #include <assert.h>
18
+
19
+
20
+ //=====================================================================
21
+ // 32BIT INTEGER DEFINITION
22
+ //=====================================================================
23
+ #ifndef __INTEGER_32_BITS__
24
+ #define __INTEGER_32_BITS__
25
+ #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
26
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
27
+ defined(_M_AMD64)
28
+ typedef unsigned int ISTDUINT32;
29
+ typedef int ISTDINT32;
30
+ #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
31
+ defined(__i386) || defined(_M_X86)
32
+ typedef unsigned long ISTDUINT32;
33
+ typedef long ISTDINT32;
34
+ #elif defined(__MACOS__)
35
+ typedef UInt32 ISTDUINT32;
36
+ typedef SInt32 ISTDINT32;
37
+ #elif defined(__APPLE__) && defined(__MACH__)
38
+ #include <sys/types.h>
39
+ typedef u_int32_t ISTDUINT32;
40
+ typedef int32_t ISTDINT32;
41
+ #elif defined(__BEOS__)
42
+ #include <sys/inttypes.h>
43
+ typedef u_int32_t ISTDUINT32;
44
+ typedef int32_t ISTDINT32;
45
+ #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
46
+ typedef unsigned __int32 ISTDUINT32;
47
+ typedef __int32 ISTDINT32;
48
+ #elif defined(__GNUC__)
49
+ #include <stdint.h>
50
+ typedef uint32_t ISTDUINT32;
51
+ typedef int32_t ISTDINT32;
52
+ #else
53
+ typedef unsigned long ISTDUINT32;
54
+ typedef long ISTDINT32;
55
+ #endif
56
+ #endif
57
+
58
+
59
+ //=====================================================================
60
+ // Integer Definition
61
+ //=====================================================================
62
+ #ifndef __IINT8_DEFINED
63
+ #define __IINT8_DEFINED
64
+ typedef char IINT8;
65
+ #endif
66
+
67
+ #ifndef __IUINT8_DEFINED
68
+ #define __IUINT8_DEFINED
69
+ typedef unsigned char IUINT8;
70
+ #endif
71
+
72
+ #ifndef __IUINT16_DEFINED
73
+ #define __IUINT16_DEFINED
74
+ typedef unsigned short IUINT16;
75
+ #endif
76
+
77
+ #ifndef __IINT16_DEFINED
78
+ #define __IINT16_DEFINED
79
+ typedef short IINT16;
80
+ #endif
81
+
82
+ #ifndef __IINT32_DEFINED
83
+ #define __IINT32_DEFINED
84
+ typedef ISTDINT32 IINT32;
85
+ #endif
86
+
87
+ #ifndef __IUINT32_DEFINED
88
+ #define __IUINT32_DEFINED
89
+ typedef ISTDUINT32 IUINT32;
90
+ #endif
91
+
92
+ #ifndef __IINT64_DEFINED
93
+ #define __IINT64_DEFINED
94
+ #if defined(_MSC_VER) || defined(__BORLANDC__)
95
+ typedef __int64 IINT64;
96
+ #else
97
+ typedef long long IINT64;
98
+ #endif
99
+ #endif
100
+
101
+ #ifndef __IUINT64_DEFINED
102
+ #define __IUINT64_DEFINED
103
+ #if defined(_MSC_VER) || defined(__BORLANDC__)
104
+ typedef unsigned __int64 IUINT64;
105
+ #else
106
+ typedef unsigned long long IUINT64;
107
+ #endif
108
+ #endif
109
+
110
+ #ifndef INLINE
111
+ #if defined(__GNUC__)
112
+
113
+ #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
114
+ #define INLINE __inline__ __attribute__((always_inline))
115
+ #else
116
+ #define INLINE __inline__
117
+ #endif
118
+
119
+ #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
120
+ #define INLINE __inline
121
+ #else
122
+ #define INLINE
123
+ #endif
124
+ #endif
125
+
126
+ #if (!defined(__cplusplus)) && (!defined(inline))
127
+ #define inline INLINE
128
+ #endif
129
+
130
+
131
+ //=====================================================================
132
+ // QUEUE DEFINITION
133
+ //=====================================================================
134
+ #ifndef __IQUEUE_DEF__
135
+ #define __IQUEUE_DEF__
136
+
137
+ struct IQUEUEHEAD {
138
+ struct IQUEUEHEAD *next, *prev;
139
+ };
140
+
141
+ typedef struct IQUEUEHEAD iqueue_head;
142
+
143
+
144
+ //---------------------------------------------------------------------
145
+ // queue init
146
+ //---------------------------------------------------------------------
147
+ #define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
148
+ #define IQUEUE_HEAD(name) \
149
+ struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
150
+
151
+ #define IQUEUE_INIT(ptr) ( \
152
+ (ptr)->next = (ptr), (ptr)->prev = (ptr))
153
+
154
+ #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
155
+
156
+ #define ICONTAINEROF(ptr, type, member) ( \
157
+ (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
158
+
159
+ #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
160
+
161
+
162
+ //---------------------------------------------------------------------
163
+ // queue operation
164
+ //---------------------------------------------------------------------
165
+ #define IQUEUE_ADD(node, head) ( \
166
+ (node)->prev = (head), (node)->next = (head)->next, \
167
+ (head)->next->prev = (node), (head)->next = (node))
168
+
169
+ #define IQUEUE_ADD_TAIL(node, head) ( \
170
+ (node)->prev = (head)->prev, (node)->next = (head), \
171
+ (head)->prev->next = (node), (head)->prev = (node))
172
+
173
+ #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
174
+
175
+ #define IQUEUE_DEL(entry) (\
176
+ (entry)->next->prev = (entry)->prev, \
177
+ (entry)->prev->next = (entry)->next, \
178
+ (entry)->next = 0, (entry)->prev = 0)
179
+
180
+ #define IQUEUE_DEL_INIT(entry) do { \
181
+ IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
182
+
183
+ #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
184
+
185
+ #define iqueue_init IQUEUE_INIT
186
+ #define iqueue_entry IQUEUE_ENTRY
187
+ #define iqueue_add IQUEUE_ADD
188
+ #define iqueue_add_tail IQUEUE_ADD_TAIL
189
+ #define iqueue_del IQUEUE_DEL
190
+ #define iqueue_del_init IQUEUE_DEL_INIT
191
+ #define iqueue_is_empty IQUEUE_IS_EMPTY
192
+
193
+ #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
194
+ for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
195
+ &((iterator)->MEMBER) != (head); \
196
+ (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
197
+
198
+ #define iqueue_foreach(iterator, head, TYPE, MEMBER) \
199
+ IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
200
+
201
+ #define iqueue_foreach_entry(pos, head) \
202
+ for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
203
+
204
+
205
+ #define __iqueue_splice(list, head) do { \
206
+ iqueue_head *first = (list)->next, *last = (list)->prev; \
207
+ iqueue_head *at = (head)->next; \
208
+ (first)->prev = (head), (head)->next = (first); \
209
+ (last)->next = (at), (at)->prev = (last); } while (0)
210
+
211
+ #define iqueue_splice(list, head) do { \
212
+ if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
213
+
214
+ #define iqueue_splice_init(list, head) do { \
215
+ iqueue_splice(list, head); iqueue_init(list); } while (0)
216
+
217
+
218
+ #ifdef _MSC_VER
219
+ #pragma warning(disable:4311)
220
+ #pragma warning(disable:4312)
221
+ #pragma warning(disable:4996)
222
+ #endif
223
+
224
+ #endif
225
+
226
+
227
+ //---------------------------------------------------------------------
228
+ // BYTE ORDER & ALIGNMENT
229
+ //---------------------------------------------------------------------
230
+ #ifndef IWORDS_BIG_ENDIAN
231
+ #ifdef _BIG_ENDIAN_
232
+ #if _BIG_ENDIAN_
233
+ #define IWORDS_BIG_ENDIAN 1
234
+ #endif
235
+ #endif
236
+ #ifndef IWORDS_BIG_ENDIAN
237
+ #if defined(__hppa__) || \
238
+ defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
239
+ (defined(__MIPS__) && defined(__MIPSEB__)) || \
240
+ defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
241
+ defined(__sparc__) || defined(__powerpc__) || \
242
+ defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
243
+ #define IWORDS_BIG_ENDIAN 1
244
+ #endif
245
+ #endif
246
+ #ifndef IWORDS_BIG_ENDIAN
247
+ #define IWORDS_BIG_ENDIAN 0
248
+ #endif
249
+ #endif
250
+
251
+ #ifndef IWORDS_MUST_ALIGN
252
+ #if defined(__i386__) || defined(__i386) || defined(_i386_)
253
+ #define IWORDS_MUST_ALIGN 0
254
+ #elif defined(_M_IX86) || defined(_X86_) || defined(__x86_64__)
255
+ #define IWORDS_MUST_ALIGN 0
256
+ #elif defined(__amd64) || defined(__amd64__)
257
+ #define IWORDS_MUST_ALIGN 0
258
+ #else
259
+ #define IWORDS_MUST_ALIGN 1
260
+ #endif
261
+ #endif
262
+
263
+
264
+ //=====================================================================
265
+ // Predefine struct
266
+ //=====================================================================
267
+ struct IKCPCB;
268
+ typedef struct IKCPCB ikcpcb;
269
+
270
+
271
+ //=====================================================================
272
+ // SEGMENT
273
+ //=====================================================================
274
+ struct IKCPSEG
275
+ {
276
+ struct IQUEUEHEAD node;
277
+ IUINT32 conv;
278
+ IUINT32 cmd;
279
+ IUINT32 frg;
280
+ IUINT32 wnd;
281
+ IUINT32 ts;
282
+ IUINT32 sn;
283
+ IUINT32 una;
284
+ IUINT32 len;
285
+ IUINT32 resendts;
286
+ IUINT32 rto;
287
+ IUINT32 fastack;
288
+ IUINT32 xmit;
289
+ char data[1];
290
+ };
291
+
292
+
293
+ //---------------------------------------------------------------------
294
+ // IKCPOPS - pluggable congestion control operations
295
+ //---------------------------------------------------------------------
296
+ struct IKCPOPS
297
+ {
298
+ const char *name;
299
+ int (*init)(ikcpcb *kcp);
300
+ void (*release)(ikcpcb *kcp);
301
+ void (*on_ack)(ikcpcb *kcp, IUINT32 acked_segs, IUINT32 acked_bytes,
302
+ IUINT32 prior_in_flight);
303
+ void (*on_fast_retransmit)(ikcpcb *kcp, IUINT32 fast_retrans,
304
+ IUINT32 inflight, IUINT32 prior_cwnd);
305
+ void (*on_timeout)(ikcpcb *kcp, IUINT32 prior_cwnd);
306
+ void (*on_tick)(ikcpcb *kcp);
307
+ void (*on_app_limited)(ikcpcb *kcp, IUINT32 inflight);
308
+ void (*on_rtt)(ikcpcb *kcp, IINT32 rtt);
309
+ void (*on_pkt_sent)(ikcpcb *kcp, IUINT32 sn, IUINT32 ts,
310
+ IUINT32 len, IUINT32 inflight, IUINT32 xmit);
311
+ void (*on_pkt_acked)(ikcpcb *kcp, IUINT32 sn, IUINT32 ts,
312
+ IUINT32 len, IINT32 rtt, IUINT32 xmit);
313
+ IUINT32 (*get_info)(ikcpcb *kcp, void *buf, IUINT32 bufsize);
314
+ IUINT32 (*pacing_rate)(ikcpcb *kcp);
315
+ };
316
+
317
+
318
+ //---------------------------------------------------------------------
319
+ // IKCPCB
320
+ //---------------------------------------------------------------------
321
+ struct IKCPCB
322
+ {
323
+ IUINT32 conv, mtu, mss, state;
324
+ IUINT32 snd_una, snd_nxt, rcv_nxt;
325
+ IUINT32 ts_recent, ts_lastack, ssthresh;
326
+ IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
327
+ IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
328
+ IUINT32 current, interval, ts_flush, xmit;
329
+ IUINT32 nrcv_buf, nsnd_buf;
330
+ IUINT32 nrcv_que, nsnd_que;
331
+ IUINT32 nodelay, updated;
332
+ IUINT32 ts_probe, probe_wait;
333
+ IUINT32 dead_link, incr;
334
+ struct IQUEUEHEAD snd_queue;
335
+ struct IQUEUEHEAD rcv_queue;
336
+ struct IQUEUEHEAD snd_buf;
337
+ struct IQUEUEHEAD rcv_buf;
338
+ IUINT32 *acklist;
339
+ IUINT32 ackcount;
340
+ IUINT32 ackblock;
341
+ IUINT32 ackedlen;
342
+ void *user;
343
+ char *buffer;
344
+ int fastresend;
345
+ int fastlimit;
346
+ int nocwnd, stream;
347
+ const struct IKCPOPS *ccops;
348
+ void *congest;
349
+ int logmask;
350
+ int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
351
+ void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
352
+ };
353
+
354
+
355
+ #define IKCP_LOG_OUTPUT 1
356
+ #define IKCP_LOG_INPUT 2
357
+ #define IKCP_LOG_SEND 4
358
+ #define IKCP_LOG_RECV 8
359
+ #define IKCP_LOG_IN_DATA 16
360
+ #define IKCP_LOG_IN_ACK 32
361
+ #define IKCP_LOG_IN_PROBE 64
362
+ #define IKCP_LOG_IN_WINS 128
363
+ #define IKCP_LOG_OUT_DATA 256
364
+ #define IKCP_LOG_OUT_ACK 512
365
+ #define IKCP_LOG_OUT_PROBE 1024
366
+ #define IKCP_LOG_OUT_WINS 2048
367
+
368
+ #ifdef __cplusplus
369
+ extern "C" {
370
+ #endif
371
+
372
+ //---------------------------------------------------------------------
373
+ // interface
374
+ //---------------------------------------------------------------------
375
+
376
+ // create a new kcp control object, 'conv' must be equal in both endpoints
377
+ // of the same connection. 'user' will be passed to the output callback.
378
+ // output callback can be set up like this: 'kcp->output = my_udp_output'
379
+ ikcpcb* ikcp_create(IUINT32 conv, void *user);
380
+
381
+ // release kcp control object
382
+ void ikcp_release(ikcpcb *kcp);
383
+
384
+ // set output callback, which will be invoked by kcp
385
+ void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
386
+ ikcpcb *kcp, void *user));
387
+
388
+ // user/upper level recv: returns size, returns below zero for EAGAIN
389
+ int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
390
+
391
+ // user/upper level send, returns below zero for error
392
+ int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
393
+
394
+ // update state (call it repeatedly, every 10ms-100ms), or you can ask
395
+ // ikcp_check when to call it again (without ikcp_input/_send calling).
396
+ // 'current' - current timestamp in millisec.
397
+ void ikcp_update(ikcpcb *kcp, IUINT32 current);
398
+
399
+ // Determines when you should invoke ikcp_update next:
400
+ // returns the timestamp (in milliseconds) at which you should call
401
+ // ikcp_update, assuming no ikcp_input/_send calls occur in between.
402
+ // You can call ikcp_update at that time instead of calling it repeatedly.
403
+ // Important for reducing unnecessary ikcp_update invocations. Use it to
404
+ // schedule ikcp_update (e.g., implementing an epoll-like mechanism,
405
+ // or optimizing ikcp_update when handling massive kcp connections).
406
+ IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
407
+
408
+ // when you receive a low-level packet (e.g., UDP packet), call this
409
+ int ikcp_input(ikcpcb *kcp, const char *data, long size);
410
+
411
+ // flush pending data
412
+ void ikcp_flush(ikcpcb *kcp);
413
+
414
+ // check the size of next message in the recv queue
415
+ int ikcp_peeksize(const ikcpcb *kcp);
416
+
417
+ // change MTU size, default is 1400
418
+ int ikcp_setmtu(ikcpcb *kcp, int mtu);
419
+
420
+ // set maximum window size: sndwnd=32, rcvwnd=32 by default
421
+ int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
422
+
423
+ // get how many packets are waiting to be sent
424
+ int ikcp_waitsnd(const ikcpcb *kcp);
425
+
426
+ // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
427
+ // nodelay: 0:disable (default), 1:enable
428
+ // interval: internal update timer interval in ms, default is 100ms
429
+ // resend: 0:disable fast resend (default), 1:enable fast resend
430
+ // nc: 0:normal congestion control (default), 1:disable congestion control
431
+ int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
432
+
433
+ // install congestion control algorithm, NULL restores builtin
434
+ int ikcp_setcc(ikcpcb *kcp, const struct IKCPOPS *ops);
435
+
436
+ // write log with kcp->writelog
437
+ void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
438
+
439
+ // setup allocator
440
+ void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
441
+
442
+ // read conv
443
+ IUINT32 ikcp_getconv(const void *ptr);
444
+
445
+
446
+ #ifdef __cplusplus
447
+ }
448
+ #endif
449
+
450
+ #endif
451
+
452
+
@@ -0,0 +1,137 @@
1
+ // kcp_native.c — native Ruby extension wrapping KCP (ikcp.c) with a pull-model
2
+ // API so Ruby never needs C->Ruby callbacks. The kcp context is wrapped with
3
+ // TypedData_Wrap_Struct, which is safe on 32/64-bit and Windows (no pointer
4
+ // truncation).
5
+
6
+ #include "ruby.h"
7
+ #include "ikcp.h"
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+
11
+ typedef struct {
12
+ ikcpcb *kcp;
13
+ char *out;
14
+ int outlen;
15
+ int outcap;
16
+ } kcp_ctx;
17
+
18
+ /* Max bytes returned per output() call -- safely under the 64KB UDP datagram
19
+ limit, so a burst of KCP packets never overflows a single sendto(). */
20
+ #define KCP_MAX_BLOB 60000
21
+
22
+ static int shim_output(const char *buf, int len, ikcpcb *kcp, void *user) {
23
+ kcp_ctx *ctx = (kcp_ctx *)user;
24
+ if (ctx->outlen + len > ctx->outcap) {
25
+ ctx->outcap = (ctx->outlen + len) * 2 + 256;
26
+ ctx->out = (char *)realloc(ctx->out, ctx->outcap);
27
+ }
28
+ memcpy(ctx->out + ctx->outlen, buf, len);
29
+ ctx->outlen += len;
30
+ return 0;
31
+ }
32
+
33
+ static void ctx_free(void *ptr) {
34
+ kcp_ctx *ctx = (kcp_ctx *)ptr;
35
+ if (ctx) {
36
+ if (ctx->kcp) ikcp_release(ctx->kcp);
37
+ free(ctx->out);
38
+ free(ctx);
39
+ }
40
+ }
41
+
42
+ static const rb_data_type_t kcp_ctx_type = {
43
+ "mnet/kcp_ctx",
44
+ {0, ctx_free, 0,},
45
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
46
+ };
47
+
48
+ static kcp_ctx *get_ctx(VALUE self) {
49
+ kcp_ctx *ctx;
50
+ TypedData_Get_Struct(self, kcp_ctx, &kcp_ctx_type, ctx);
51
+ return ctx;
52
+ }
53
+
54
+ static VALUE m_kcp_new(VALUE mod, VALUE conv) {
55
+ kcp_ctx *ctx = (kcp_ctx *)calloc(1, sizeof(kcp_ctx));
56
+ ctx->kcp = ikcp_create(NUM2UINT(conv), ctx);
57
+ ikcp_setoutput(ctx->kcp, shim_output);
58
+ ikcp_nodelay(ctx->kcp, 1, 10, 2, 1);
59
+ ikcp_wndsize(ctx->kcp, 128, 128);
60
+ ctx->kcp->stream = 1; /* byte-stream mode */
61
+ return TypedData_Wrap_Struct(rb_cObject, &kcp_ctx_type, ctx);
62
+ }
63
+
64
+ static VALUE m_kcp_send(VALUE mod, VALUE self, VALUE data) {
65
+ kcp_ctx *ctx = get_ctx(self);
66
+ StringValue(data);
67
+ return INT2NUM(ikcp_send(ctx->kcp, RSTRING_PTR(data), (int)RSTRING_LEN(data)));
68
+ }
69
+
70
+ static VALUE m_kcp_recv(VALUE mod, VALUE self, VALUE maxlen) {
71
+ kcp_ctx *ctx = get_ctx(self);
72
+ int n = NUM2INT(maxlen);
73
+ char *tmp = (char *)malloc((size_t)n);
74
+ if (!tmp) rb_raise(rb_eNoMemError, "kcp recv buffer");
75
+ int got = ikcp_recv(ctx->kcp, tmp, n);
76
+ if (got <= 0) {
77
+ free(tmp);
78
+ return Qnil;
79
+ }
80
+ VALUE buf = rb_str_new(tmp, got);
81
+ free(tmp);
82
+ return buf;
83
+ }
84
+
85
+ static VALUE m_kcp_input(VALUE mod, VALUE self, VALUE data) {
86
+ kcp_ctx *ctx = get_ctx(self);
87
+ StringValue(data);
88
+ return INT2NUM(ikcp_input(ctx->kcp, RSTRING_PTR(data), (long)RSTRING_LEN(data)));
89
+ }
90
+
91
+ static VALUE m_kcp_update(VALUE mod, VALUE self, VALUE now) {
92
+ kcp_ctx *ctx = get_ctx(self);
93
+ ikcp_update(ctx->kcp, NUM2UINT(now));
94
+ return Qnil;
95
+ }
96
+
97
+ static VALUE m_kcp_flush(VALUE mod, VALUE self) {
98
+ kcp_ctx *ctx = get_ctx(self);
99
+ /* ikcp_flush returns early if ikcp_update hasn't run yet; initialize the
100
+ flag so a bare send+flush works without a prior update call. */
101
+ if (ctx->kcp->updated == 0) {
102
+ ctx->kcp->updated = 1;
103
+ ctx->kcp->ts_flush = ctx->kcp->current;
104
+ }
105
+ ikcp_flush(ctx->kcp);
106
+ return Qnil;
107
+ }
108
+
109
+ static VALUE m_kcp_output(VALUE mod, VALUE self) {
110
+ kcp_ctx *ctx = get_ctx(self);
111
+ if (ctx->outlen <= 0) return Qnil;
112
+ /* 一次最多返回 60KB 拼接好的包,避免单个 UDP 数据报超过 64KB(EMSGSIZE) */
113
+ int n = ctx->outlen < KCP_MAX_BLOB ? ctx->outlen : KCP_MAX_BLOB;
114
+ VALUE buf = rb_str_new(ctx->out, n);
115
+ if (n < ctx->outlen) {
116
+ memmove(ctx->out, ctx->out + n, ctx->outlen - n);
117
+ }
118
+ ctx->outlen -= n;
119
+ return buf;
120
+ }
121
+
122
+ static VALUE m_kcp_waitsnd(VALUE mod, VALUE self) {
123
+ kcp_ctx *ctx = get_ctx(self);
124
+ return INT2NUM(ikcp_waitsnd(ctx->kcp));
125
+ }
126
+
127
+ void Init_kcp_native(void) {
128
+ VALUE m = rb_define_module("KcpNative");
129
+ rb_define_singleton_method(m, "kcp_new", m_kcp_new, 1);
130
+ rb_define_singleton_method(m, "kcp_send", m_kcp_send, 2);
131
+ rb_define_singleton_method(m, "kcp_recv", m_kcp_recv, 2);
132
+ rb_define_singleton_method(m, "kcp_input", m_kcp_input, 2);
133
+ rb_define_singleton_method(m, "kcp_update", m_kcp_update, 2);
134
+ rb_define_singleton_method(m, "kcp_flush", m_kcp_flush, 1);
135
+ rb_define_singleton_method(m, "kcp_output", m_kcp_output, 1);
136
+ rb_define_singleton_method(m, "kcp_waitsnd", m_kcp_waitsnd, 1);
137
+ }
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kcp
4
+ VERSION = '0.1.0'
5
+ end
data/lib/kcp.rb ADDED
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ # KCP 是一个快速、可靠的 ARQ 协议(C 实现,来自 skywind3000/kcp)。
4
+ # 本 gem 通过原生扩展把 KCP 封装成「拉取式」接口,供上层传输协议使用。
5
+
6
+ require 'kcp/version'
7
+ require 'kcp_native'
8
+
9
+ module Kcp
10
+ # 可靠字节流引擎(stream 模式)。它只负责「把一段字节流可靠送达对端」,
11
+ # 不处理网络 I/O、地址、加密 —— 这些由调用方负责。
12
+ #
13
+ # 用法:
14
+ # engine = Kcp::Engine.new(conv)
15
+ # engine.send(data) # 把数据排队
16
+ # engine.flush # 立即刷出(生成 KCP 包)
17
+ # pkt = engine.output # 取出待发送的编码包,交给 UDP sendto
18
+ # engine.input(recv_pkt) # 喂入收到的编码包
19
+ # chunk = engine.recv # 取出对端送来的可靠字节流
20
+ # engine.update(now_ms) # 周期性驱动(重传/窗口更新)
21
+ class Engine
22
+ # conv 是会话标识(uint32),连接两端必须一致。
23
+ def initialize(conv)
24
+ @ctx = KcpNative.kcp_new(conv)
25
+ end
26
+
27
+ # 把数据排队等待可靠发送。返回排入的字节数,出错返回负数。
28
+ # 注意:数据不会立即发出去,需要调用 #flush(或 #update)刷出。
29
+ def send(data)
30
+ KcpNative.kcp_send(@ctx, data)
31
+ end
32
+
33
+ # 取出对端已送达的、按序的字节流(最多 maxlen 字节)。
34
+ # 没有数据时返回 nil。
35
+ def recv(maxlen = 65_536)
36
+ KcpNative.kcp_recv(@ctx, maxlen)
37
+ end
38
+
39
+ # 喂入一个收到的 KCP 编码包(低层 UDP 数据报的内容)。
40
+ def input(data)
41
+ KcpNative.kcp_input(@ctx, data)
42
+ end
43
+
44
+ # 驱动 KCP 时钟(按间隔触发的重传、ACK、窗口探测)。now_ms 是毫秒时间戳,
45
+ # 内部会截断到 32 位(KCP 用 uint32 毫秒,约 49 天回绕一次,属正常)。
46
+ def update(now_ms)
47
+ KcpNative.kcp_update(@ctx, now_ms.to_i & 0xffffffff)
48
+ end
49
+
50
+ # 立即刷出待发送数据(不受 update 的间隔节流)。
51
+ def flush
52
+ KcpNative.kcp_flush(@ctx)
53
+ end
54
+
55
+ # 取出待发送的编码包(一次一个 KCP 包),交给低层发送。没有待发送时返回 nil。
56
+ def output
57
+ KcpNative.kcp_output(@ctx)
58
+ end
59
+
60
+ # 发送队列里待发送的包数(>0 表示窗口已满、有积压)。
61
+ def waitsnd
62
+ KcpNative.kcp_waitsnd(@ctx)
63
+ end
64
+
65
+ # 释放引用(C 对象由 GC 回收)。
66
+ def close
67
+ @ctx = nil
68
+ end
69
+
70
+ private
71
+
72
+ def now_ms
73
+ (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i & 0xffffffff
74
+ end
75
+ end
76
+ end