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 +7 -0
- data/README.en.md +235 -0
- data/README.md +221 -0
- data/ext/kcp/extconf.rb +9 -0
- data/ext/kcp/ikcp.c +1423 -0
- data/ext/kcp/ikcp.h +452 -0
- data/ext/kcp/kcp_native.c +137 -0
- data/lib/kcp/version.rb +5 -0
- data/lib/kcp.rb +76 -0
- metadata +65 -0
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
|
+
}
|
data/lib/kcp/version.rb
ADDED
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
|