immigrant from my original project http://code.google.com/p/ikcp
This commit is contained in:
parent
e510747b5e
commit
09e1a3495d
379
ikcp.h
Normal file
379
ikcp.h
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
//=====================================================================
|
||||||
|
//
|
||||||
|
// KCP - A Better ARQ Protocol Implementation
|
||||||
|
// skywind3000 (at) gmail.com, 2010-2011
|
||||||
|
//
|
||||||
|
// Features:
|
||||||
|
// + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
|
||||||
|
// + Maximum RTT reduce three times vs tcp.
|
||||||
|
// + Lightweight, distributed as a single source file.
|
||||||
|
//
|
||||||
|
//=====================================================================
|
||||||
|
#ifndef __IKCP_H__
|
||||||
|
#define __IKCP_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
//=====================================================================
|
||||||
|
// 32BIT INTEGER DEFINITION
|
||||||
|
//=====================================================================
|
||||||
|
#ifndef __INTEGER_32_BITS__
|
||||||
|
#define __INTEGER_32_BITS__
|
||||||
|
#if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
|
||||||
|
defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
|
||||||
|
defined(_M_AMD64)
|
||||||
|
typedef unsigned int ISTDUINT32;
|
||||||
|
typedef int ISTDINT32;
|
||||||
|
#elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
|
||||||
|
defined(__i386) || defined(_M_X86)
|
||||||
|
typedef unsigned long ISTDUINT32;
|
||||||
|
typedef long ISTDINT32;
|
||||||
|
#elif defined(__MACOS__)
|
||||||
|
typedef UInt32 ISTDUINT32;
|
||||||
|
typedef SInt32 ISTDINT32;
|
||||||
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
|
#include <sys/types.h>
|
||||||
|
typedef u_int32_t ISTDUINT32;
|
||||||
|
typedef int32_t ISTDINT32;
|
||||||
|
#elif defined(__BEOS__)
|
||||||
|
#include <sys/inttypes.h>
|
||||||
|
typedef u_int32_t ISTDUINT32;
|
||||||
|
typedef int32_t ISTDINT32;
|
||||||
|
#elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
|
||||||
|
typedef unsigned __int32 ISTDUINT32;
|
||||||
|
typedef __int32 ISTDINT32;
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef uint32_t ISTDUINT32;
|
||||||
|
typedef int32_t ISTDINT32;
|
||||||
|
#else
|
||||||
|
typedef unsigned long ISTDUINT32;
|
||||||
|
typedef long ISTDINT32;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//=====================================================================
|
||||||
|
// Integer Definition
|
||||||
|
//=====================================================================
|
||||||
|
#ifndef __IINT8_DEFINED
|
||||||
|
#define __IINT8_DEFINED
|
||||||
|
typedef char IINT8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IUINT8_DEFINED
|
||||||
|
#define __IUINT8_DEFINED
|
||||||
|
typedef unsigned char IUINT8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IUINT16_DEFINED
|
||||||
|
#define __IUINT16_DEFINED
|
||||||
|
typedef unsigned short IUINT16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IINT16_DEFINED
|
||||||
|
#define __IINT16_DEFINED
|
||||||
|
typedef short IINT16;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IINT32_DEFINED
|
||||||
|
#define __IINT32_DEFINED
|
||||||
|
typedef ISTDINT32 IINT32;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IUINT32_DEFINED
|
||||||
|
#define __IUINT32_DEFINED
|
||||||
|
typedef ISTDUINT32 IUINT32;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IINT64_DEFINED
|
||||||
|
#define __IINT64_DEFINED
|
||||||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
typedef __int64 IINT64;
|
||||||
|
#else
|
||||||
|
typedef long long IINT64;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef __IUINT64_DEFINED
|
||||||
|
#define __IUINT64_DEFINED
|
||||||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
typedef unsigned __int64 IUINT64;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long IUINT64;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef INLINE
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
|
||||||
|
#if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
|
||||||
|
#define INLINE __inline__ __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define INLINE __inline__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
|
||||||
|
#define INLINE __inline
|
||||||
|
#else
|
||||||
|
#define INLINE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef inline
|
||||||
|
#define inline INLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//=====================================================================
|
||||||
|
// QUEUE DEFINITION
|
||||||
|
//=====================================================================
|
||||||
|
#ifndef __IQUEUE_DEF__
|
||||||
|
#define __IQUEUE_DEF__
|
||||||
|
|
||||||
|
struct IQUEUEHEAD {
|
||||||
|
struct IQUEUEHEAD *next, *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct IQUEUEHEAD iqueue_head;
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// queue init
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
#define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
|
||||||
|
#define IQUEUE_HEAD(name) \
|
||||||
|
struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
|
||||||
|
|
||||||
|
#define IQUEUE_INIT(ptr) ( \
|
||||||
|
(ptr)->next = (ptr), (ptr)->prev = (ptr))
|
||||||
|
|
||||||
|
#define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||||
|
|
||||||
|
#define ICONTAINEROF(ptr, type, member) ( \
|
||||||
|
(type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
|
||||||
|
|
||||||
|
#define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// queue operation
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
#define IQUEUE_ADD(node, head) ( \
|
||||||
|
(node)->prev = (head), (node)->next = (head)->next, \
|
||||||
|
(head)->next->prev = (node), (head)->next = (node))
|
||||||
|
|
||||||
|
#define IQUEUE_ADD_TAIL(node, head) ( \
|
||||||
|
(node)->prev = (head)->prev, (node)->next = (head), \
|
||||||
|
(head)->prev->next = (node), (head)->prev = (node))
|
||||||
|
|
||||||
|
#define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
|
||||||
|
|
||||||
|
#define IQUEUE_DEL(entry) (\
|
||||||
|
(entry)->next->prev = (entry)->prev, \
|
||||||
|
(entry)->prev->next = (entry)->next, \
|
||||||
|
(entry)->next = 0, (entry)->prev = 0)
|
||||||
|
|
||||||
|
#define IQUEUE_DEL_INIT(entry) do { \
|
||||||
|
IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
|
||||||
|
|
||||||
|
#define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
|
||||||
|
|
||||||
|
#define iqueue_init IQUEUE_INIT
|
||||||
|
#define iqueue_entry IQUEUE_ENTRY
|
||||||
|
#define iqueue_add IQUEUE_ADD
|
||||||
|
#define iqueue_add_tail IQUEUE_ADD_TAIL
|
||||||
|
#define iqueue_del IQUEUE_DEL
|
||||||
|
#define iqueue_del_init IQUEUE_DEL_INIT
|
||||||
|
#define iqueue_is_empty IQUEUE_IS_EMPTY
|
||||||
|
|
||||||
|
#define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
|
||||||
|
for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
|
||||||
|
&((iterator)->MEMBER) != (head); \
|
||||||
|
(iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
|
||||||
|
|
||||||
|
#define iqueue_foreach(iterator, head, TYPE, MEMBER) \
|
||||||
|
IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
|
||||||
|
|
||||||
|
#define iqueue_foreach_entry(pos, head) \
|
||||||
|
for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
|
||||||
|
|
||||||
|
|
||||||
|
#define __iqueue_splice(list, head) do { \
|
||||||
|
iqueue_head *first = (list)->next, *last = (list)->prev; \
|
||||||
|
iqueue_head *at = (head)->next; \
|
||||||
|
(first)->prev = (head), (head)->next = (first); \
|
||||||
|
(last)->next = (at), (at)->prev = (last); } while (0)
|
||||||
|
|
||||||
|
#define iqueue_splice(list, head) do { \
|
||||||
|
if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
|
||||||
|
|
||||||
|
#define iqueue_splice_init(list, head) do { \
|
||||||
|
iqueue_splice(list, head); iqueue_init(list); } while (0)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(disable:4311)
|
||||||
|
#pragma warning(disable:4312)
|
||||||
|
#pragma warning(disable:4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// WORD ORDER
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
#ifndef IWORDS_BIG_ENDIAN
|
||||||
|
#ifdef _BIG_ENDIAN_
|
||||||
|
#if _BIG_ENDIAN_
|
||||||
|
#define IWORDS_BIG_ENDIAN 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef IWORDS_BIG_ENDIAN
|
||||||
|
#if defined(__hppa__) || \
|
||||||
|
defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
|
||||||
|
(defined(__MIPS__) && defined(__MISPEB__)) || \
|
||||||
|
defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
|
||||||
|
defined(__sparc__)
|
||||||
|
#define IWORDS_BIG_ENDIAN 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef IWORDS_BIG_ENDIAN
|
||||||
|
#define IWORDS_BIG_ENDIAN 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=====================================================================
|
||||||
|
// SEGMENT
|
||||||
|
//=====================================================================
|
||||||
|
struct IKCPSEG
|
||||||
|
{
|
||||||
|
struct IQUEUEHEAD node;
|
||||||
|
IUINT32 conv;
|
||||||
|
IUINT32 cmd;
|
||||||
|
IUINT32 frg;
|
||||||
|
IUINT32 wnd;
|
||||||
|
IUINT32 ts;
|
||||||
|
IUINT32 sn;
|
||||||
|
IUINT32 una;
|
||||||
|
IUINT32 len;
|
||||||
|
IUINT32 resendts;
|
||||||
|
IUINT32 rto;
|
||||||
|
IUINT32 fastack;
|
||||||
|
IUINT32 xmit;
|
||||||
|
char data[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// IKCPCB
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
struct IKCPCB
|
||||||
|
{
|
||||||
|
IUINT32 conv, mtu, mss, state;
|
||||||
|
IUINT32 snd_una, snd_nxt, rcv_nxt;
|
||||||
|
IUINT32 ts_recent, ts_lastack, ssthresh;
|
||||||
|
IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
|
||||||
|
IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
|
||||||
|
IUINT32 current, interval, ts_flush, xmit;
|
||||||
|
IUINT32 nrcv_buf, nsnd_buf;
|
||||||
|
IUINT32 nrcv_que, nsnd_que;
|
||||||
|
IUINT32 nodelay, updated;
|
||||||
|
IUINT32 ts_probe, probe_wait;
|
||||||
|
IUINT32 dead_link, incr;
|
||||||
|
struct IQUEUEHEAD snd_queue;
|
||||||
|
struct IQUEUEHEAD rcv_queue;
|
||||||
|
struct IQUEUEHEAD snd_buf;
|
||||||
|
struct IQUEUEHEAD rcv_buf;
|
||||||
|
IUINT32 *acklist;
|
||||||
|
IUINT32 ackcount;
|
||||||
|
IUINT32 ackblock;
|
||||||
|
void *user;
|
||||||
|
char *buffer;
|
||||||
|
int fastresend;
|
||||||
|
int nocwnd;
|
||||||
|
int logmask;
|
||||||
|
int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
|
||||||
|
void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct IKCPCB ikcpcb;
|
||||||
|
|
||||||
|
#define IKCP_LOG_OUTPUT 1
|
||||||
|
#define IKCP_LOG_INPUT 2
|
||||||
|
#define IKCP_LOG_SEND 4
|
||||||
|
#define IKCP_LOG_RECV 8
|
||||||
|
#define IKCP_LOG_IN_DATA 16
|
||||||
|
#define IKCP_LOG_IN_ACK 32
|
||||||
|
#define IKCP_LOG_IN_PROBE 64
|
||||||
|
#define IKCP_LOG_IN_WINS 128
|
||||||
|
#define IKCP_LOG_OUT_DATA 256
|
||||||
|
#define IKCP_LOG_OUT_ACK 512
|
||||||
|
#define IKCP_LOG_OUT_PROBE 1024
|
||||||
|
#define IKCP_LOG_OUT_WINS 2048
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// interface
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
// create a new kcp control object, 'conv' must equal in two endpoint
|
||||||
|
// from the same connection. 'user' will be passed to the output callback
|
||||||
|
// output callback can be setup like this: 'kcp->output = my_udp_output'
|
||||||
|
ikcpcb* ikcp_create(IUINT32 conv, void *user);
|
||||||
|
|
||||||
|
// release kcp control object
|
||||||
|
void ikcp_release(ikcpcb *kcp);
|
||||||
|
|
||||||
|
int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
|
||||||
|
int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
|
||||||
|
|
||||||
|
// update state (call it repeatedly, every 10ms-100ms)
|
||||||
|
// 'current' - current timestamp in millisec
|
||||||
|
void ikcp_update(ikcpcb *kcp, IUINT32 current);
|
||||||
|
IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
|
||||||
|
|
||||||
|
// when you received a low level packet (eg. UDP packet), call it
|
||||||
|
int ikcp_input(ikcpcb *kcp, const char *data, long size);
|
||||||
|
void ikcp_flush(ikcpcb *kcp);
|
||||||
|
|
||||||
|
int ikcp_peeksize(const ikcpcb *kcp);
|
||||||
|
|
||||||
|
// change MTU size, default is 14000
|
||||||
|
int ikcp_setmtu(ikcpcb *kcp, int mtu);
|
||||||
|
|
||||||
|
// set maximum window size: sndwnd=32, rcvwnd=32 by default
|
||||||
|
int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
|
||||||
|
|
||||||
|
// get how many packet is waiting to be sent
|
||||||
|
int ikcp_waitsnd(const ikcpcb *kcp);
|
||||||
|
|
||||||
|
// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
||||||
|
// nodelay: 0:disable(default), 1:enable
|
||||||
|
// interval: internal update timer interval in millisec, default is 100ms
|
||||||
|
// resend: 0:disable fast resend(default), 1:enable fast resend
|
||||||
|
// nc: 0:normal congestion control(default), 1:disable congestion control
|
||||||
|
int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
|
||||||
|
|
||||||
|
int ikcp_rcvbuf_count(const ikcpcb *kcp);
|
||||||
|
int ikcp_sndbuf_count(const ikcpcb *kcp);
|
||||||
|
|
||||||
|
void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
177
test.cpp
Normal file
177
test.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
//=====================================================================
|
||||||
|
//
|
||||||
|
// test.cpp - kcp 测试用例
|
||||||
|
//
|
||||||
|
// 说明:
|
||||||
|
// gcc test.cpp -o test -lstdc++
|
||||||
|
//
|
||||||
|
//=====================================================================
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test.h"
|
||||||
|
#include "ikcp.c"
|
||||||
|
|
||||||
|
|
||||||
|
// 模拟网络
|
||||||
|
LatencySimulator *vnet;
|
||||||
|
|
||||||
|
// 模拟网络:模拟发送一个 udp包
|
||||||
|
int udp_output(const char *buf, int len, ikcpcb *kcp, void *user)
|
||||||
|
{
|
||||||
|
int id = (int)user;
|
||||||
|
vnet->send(id, buf, len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用例
|
||||||
|
void test(int mode)
|
||||||
|
{
|
||||||
|
// 创建模拟网络:丢包率10%,Rtt 60ms~125ms
|
||||||
|
vnet = new LatencySimulator(10, 60, 125);
|
||||||
|
|
||||||
|
// 创建两个端点的 kcp对象,第一个参数 conv是会话编号,同一个会话需要相同
|
||||||
|
// 最后一个是 user参数,用来传递标识
|
||||||
|
ikcpcb *kcp1 = ikcp_create(0x11223344, (void*)0);
|
||||||
|
ikcpcb *kcp2 = ikcp_create(0x11223344, (void*)1);
|
||||||
|
|
||||||
|
// 设置kcp的下层输出,这里为 udp_output,模拟udp网络输出函数
|
||||||
|
kcp1->output = udp_output;
|
||||||
|
kcp2->output = udp_output;
|
||||||
|
|
||||||
|
IUINT32 current = iclock();
|
||||||
|
IUINT32 slap = current + 20;
|
||||||
|
IUINT32 index = 0;
|
||||||
|
IUINT32 next = 0;
|
||||||
|
IINT64 sumrtt = 0;
|
||||||
|
int count = 0;
|
||||||
|
int maxrtt = 0;
|
||||||
|
|
||||||
|
// 配置窗口大小:平均延迟200ms,每20ms发送一个包,
|
||||||
|
// 而考虑到丢包重发,设置最大收发窗口为128
|
||||||
|
ikcp_wndsize(kcp1, 128, 128);
|
||||||
|
ikcp_wndsize(kcp2, 128, 128);
|
||||||
|
|
||||||
|
// 判断测试用例的模式
|
||||||
|
if (mode == 0) {
|
||||||
|
// 默认模式
|
||||||
|
ikcp_nodelay(kcp1, 0, 10, 0, 0);
|
||||||
|
ikcp_nodelay(kcp2, 0, 10, 0, 0);
|
||||||
|
}
|
||||||
|
else if (mode == 1) {
|
||||||
|
// 普通模式,关闭流控等
|
||||||
|
ikcp_nodelay(kcp1, 0, 10, 0, 1);
|
||||||
|
ikcp_nodelay(kcp2, 0, 10, 0, 1);
|
||||||
|
} else {
|
||||||
|
// 启动快速模式
|
||||||
|
// 第二个参数 nodelay-启用以后若干常规加速将启动
|
||||||
|
// 第三个参数 interval为内部处理时钟,默认设置为 10ms
|
||||||
|
// 第四个参数 resend为快速重传指标,设置为2
|
||||||
|
// 第五个参数 为是否禁用常规流控,这里禁止
|
||||||
|
ikcp_nodelay(kcp1, 1, 10, 2, 1);
|
||||||
|
ikcp_nodelay(kcp2, 1, 10, 2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char buffer[2000];
|
||||||
|
int hr;
|
||||||
|
|
||||||
|
IUINT32 ts1 = iclock();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
isleep(1);
|
||||||
|
current = iclock();
|
||||||
|
ikcp_update(kcp1, iclock());
|
||||||
|
ikcp_update(kcp2, iclock());
|
||||||
|
|
||||||
|
// 每隔 20ms,kcp1发送数据
|
||||||
|
for (; current >= slap; slap += 20) {
|
||||||
|
*(IUINT32*)(buffer + 0) = index++;
|
||||||
|
*(IUINT32*)(buffer + 4) = current;
|
||||||
|
|
||||||
|
// 发送上层协议包
|
||||||
|
ikcp_send(kcp1, buffer, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理虚拟网络:检测是否有udp包从p1->p2
|
||||||
|
while (1) {
|
||||||
|
hr = vnet->recv(1, buffer, 2000);
|
||||||
|
if (hr < 0) break;
|
||||||
|
// 如果 p2收到udp,则作为下层协议输入到kcp2
|
||||||
|
ikcp_input(kcp2, buffer, hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理虚拟网络:检测是否有udp包从p2->p1
|
||||||
|
while (1) {
|
||||||
|
hr = vnet->recv(0, buffer, 2000);
|
||||||
|
if (hr < 0) break;
|
||||||
|
// 如果 p1收到udp,则作为下层协议输入到kcp1
|
||||||
|
ikcp_input(kcp1, buffer, hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// kcp2接收到任何包都返回回去
|
||||||
|
while (1) {
|
||||||
|
hr = ikcp_recv(kcp2, buffer, 10);
|
||||||
|
// 没有收到包就退出
|
||||||
|
if (hr < 0) break;
|
||||||
|
// 如果收到包就回射
|
||||||
|
ikcp_send(kcp2, buffer, hr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// kcp1收到kcp2的回射数据
|
||||||
|
while (1) {
|
||||||
|
hr = ikcp_recv(kcp1, buffer, 10);
|
||||||
|
// 没有收到包就退出
|
||||||
|
if (hr < 0) break;
|
||||||
|
IUINT32 sn = *(IUINT32*)(buffer + 0);
|
||||||
|
IUINT32 ts = *(IUINT32*)(buffer + 4);
|
||||||
|
IUINT32 rtt = current - ts;
|
||||||
|
|
||||||
|
if (sn != next) {
|
||||||
|
// 如果收到的包不连续
|
||||||
|
printf("ERROR sn %d<->%d\n", (int)count, (int)next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next++;
|
||||||
|
sumrtt += rtt;
|
||||||
|
count++;
|
||||||
|
if (rtt > maxrtt) maxrtt = rtt;
|
||||||
|
|
||||||
|
printf("[RECV] mode=%d sn=%d rtt=%d\n", mode, (int)sn, (int)rtt);
|
||||||
|
}
|
||||||
|
if (next > 1000) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts1 = iclock() - ts1;
|
||||||
|
|
||||||
|
ikcp_release(kcp1);
|
||||||
|
ikcp_release(kcp2);
|
||||||
|
|
||||||
|
const char *names[3] = { "default", "normal", "fast" };
|
||||||
|
printf("%s mode result (%dms):\n", names[mode], ts1);
|
||||||
|
printf("avgrtt=%d maxrtt=%d\n", (int)(sumrtt / count), maxrtt);
|
||||||
|
printf("press enter to next ...\n");
|
||||||
|
char ch; scanf("%c", &ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test(0); // 默认模式,类似 TCP:正常模式,无快速重传,常规流控
|
||||||
|
test(1); // 普通模式,关闭流控等
|
||||||
|
test(2); // 快速模式,所有开关都打开,且关闭流控
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
default mode result (20917ms):
|
||||||
|
avgrtt=740 maxrtt=1507
|
||||||
|
|
||||||
|
normal mode result (20131ms):
|
||||||
|
avgrtt=156 maxrtt=571
|
||||||
|
|
||||||
|
fast mode result (20207ms):
|
||||||
|
avgrtt=138 maxrtt=392
|
||||||
|
*/
|
||||||
|
|
241
test.h
Normal file
241
test.h
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
#ifndef __TEST_H__
|
||||||
|
#define __TEST_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "ikcp.h"
|
||||||
|
|
||||||
|
#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
|
||||||
|
#include <windows.h>
|
||||||
|
#elif !defined(__unix)
|
||||||
|
#define __unix
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __unix
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* get system time */
|
||||||
|
static inline void itimeofday(long *sec, long *usec)
|
||||||
|
{
|
||||||
|
#if defined(__unix)
|
||||||
|
struct timeval time;
|
||||||
|
gettimeofday(&time, NULL);
|
||||||
|
if (sec) *sec = time.tv_sec;
|
||||||
|
if (usec) *usec = time.tv_usec;
|
||||||
|
#else
|
||||||
|
static long mode = 0, addsec = 0;
|
||||||
|
BOOL retval;
|
||||||
|
static IINT64 freq = 1;
|
||||||
|
IINT64 qpc;
|
||||||
|
if (mode == 0) {
|
||||||
|
retval = QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
|
||||||
|
freq = (freq == 0)? 1 : freq;
|
||||||
|
retval = QueryPerformanceCounter((LARGE_INTEGER*)&qpc);
|
||||||
|
addsec = (long)time(NULL);
|
||||||
|
addsec = addsec - (long)((qpc / freq) & 0x7fffffff);
|
||||||
|
mode = 1;
|
||||||
|
}
|
||||||
|
retval = QueryPerformanceCounter((LARGE_INTEGER*)&qpc);
|
||||||
|
retval = retval * 2;
|
||||||
|
if (sec) *sec = (long)(qpc / freq) + addsec;
|
||||||
|
if (usec) *usec = (long)((qpc % freq) * 1000000 / freq);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get clock in millisecond 64 */
|
||||||
|
static inline IINT64 iclock64(void)
|
||||||
|
{
|
||||||
|
long s, u;
|
||||||
|
IINT64 value;
|
||||||
|
itimeofday(&s, &u);
|
||||||
|
value = ((IINT64)s) * 1000 + (u / 1000);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline IUINT32 iclock()
|
||||||
|
{
|
||||||
|
return (IUINT32)(iclock64() & 0xfffffffful);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sleep in millisecond */
|
||||||
|
static inline void isleep(unsigned long millisecond)
|
||||||
|
{
|
||||||
|
#ifdef __unix /* usleep( time * 1000 ); */
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = (time_t)(millisecond / 1000);
|
||||||
|
ts.tv_nsec = (long)((millisecond % 1000) * 1000000);
|
||||||
|
/*nanosleep(&ts, NULL);*/
|
||||||
|
usleep((millisecond << 10) - (millisecond << 4) - (millisecond << 3));
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
Sleep(millisecond);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// 带延迟的数据包
|
||||||
|
class DelayPacket
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~DelayPacket() {
|
||||||
|
if (_ptr) delete _ptr;
|
||||||
|
_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayPacket(int size, const void *src = NULL) {
|
||||||
|
_ptr = new unsigned char[size];
|
||||||
|
_size = size;
|
||||||
|
if (src) {
|
||||||
|
memcpy(_ptr, src, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* ptr() { return _ptr; }
|
||||||
|
const unsigned char* ptr() const { return _ptr; }
|
||||||
|
|
||||||
|
int size() const { return _size; }
|
||||||
|
IUINT32 ts() const { return _ts; }
|
||||||
|
void setts(IUINT32 ts) { _ts = ts; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unsigned char *_ptr;
|
||||||
|
int _size;
|
||||||
|
IUINT32 _ts;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 均匀分布的随机数
|
||||||
|
class Random
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Random(int size) {
|
||||||
|
this->size = 0;
|
||||||
|
seeds.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int random() {
|
||||||
|
int x, i;
|
||||||
|
if (seeds.size() == 0) return 0;
|
||||||
|
if (size == 0) {
|
||||||
|
for (i = 0; i < (int)seeds.size(); i++) {
|
||||||
|
seeds[i] = i;
|
||||||
|
}
|
||||||
|
size = (int)seeds.size();
|
||||||
|
}
|
||||||
|
i = rand() % size;
|
||||||
|
x = seeds[i];
|
||||||
|
seeds[i] = seeds[--size];
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int size;
|
||||||
|
std::vector<int> seeds;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 网络延迟模拟器
|
||||||
|
class LatencySimulator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~LatencySimulator() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// lostrate: 往返一周丢包率的百分比,默认 10%
|
||||||
|
// rttmin:rtt最小值,默认 60
|
||||||
|
// rttmax:rtt最大值,默认 125
|
||||||
|
LatencySimulator(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000):
|
||||||
|
r12(100), r21(100) {
|
||||||
|
current = iclock();
|
||||||
|
this->lostrate = lostrate / 2; // 上面数据是往返丢包率,单程除以2
|
||||||
|
this->rttmin = rttmin / 2;
|
||||||
|
this->rttmax = rttmax / 2;
|
||||||
|
this->nmax = nmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除数据
|
||||||
|
void clear() {
|
||||||
|
DelayTunnel::iterator it;
|
||||||
|
for (it = p12.begin(); it != p12.end(); it++) {
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
for (it = p21.begin(); it != p21.end(); it++) {
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
p12.clear();
|
||||||
|
p21.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送数据
|
||||||
|
// peer - 端点0/1,从0发送,从1接收;从1发送从0接收
|
||||||
|
void send(int peer, const void *data, int size) {
|
||||||
|
if (peer == 0) {
|
||||||
|
if (r12.random() < lostrate) return;
|
||||||
|
if ((int)p12.size() >= nmax) return;
|
||||||
|
} else {
|
||||||
|
if (r21.random() < lostrate) return;
|
||||||
|
if ((int)p21.size() >= nmax) return;
|
||||||
|
}
|
||||||
|
DelayPacket *pkt = new DelayPacket(size, data);
|
||||||
|
current = iclock();
|
||||||
|
IUINT32 delay = rttmin;
|
||||||
|
if (rttmax > rttmin) delay += rand() % (rttmax - rttmin);
|
||||||
|
pkt->setts(current + delay);
|
||||||
|
if (peer == 0) {
|
||||||
|
p12.push_back(pkt);
|
||||||
|
} else {
|
||||||
|
p21.push_back(pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接收数据
|
||||||
|
int recv(int peer, void *data, int maxsize) {
|
||||||
|
DelayTunnel::iterator it;
|
||||||
|
if (peer == 0) {
|
||||||
|
it = p21.begin();
|
||||||
|
if (p21.size() == 0) return -1;
|
||||||
|
} else {
|
||||||
|
it = p12.begin();
|
||||||
|
if (p12.size() == 0) return -1;
|
||||||
|
}
|
||||||
|
DelayPacket *pkt = *it;
|
||||||
|
current = iclock();
|
||||||
|
if (current < pkt->ts()) return -2;
|
||||||
|
if (maxsize < pkt->size()) return -3;
|
||||||
|
if (peer == 0) {
|
||||||
|
p21.erase(it);
|
||||||
|
} else {
|
||||||
|
p12.erase(it);
|
||||||
|
}
|
||||||
|
maxsize = pkt->size();
|
||||||
|
memcpy(data, pkt->ptr(), maxsize);
|
||||||
|
return maxsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IUINT32 current;
|
||||||
|
int lostrate;
|
||||||
|
int rttmin;
|
||||||
|
int rttmax;
|
||||||
|
int nmax;
|
||||||
|
typedef std::list<DelayPacket*> DelayTunnel;
|
||||||
|
DelayTunnel p12;
|
||||||
|
DelayTunnel p21;
|
||||||
|
Random r12;
|
||||||
|
Random r21;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user