mirror of
https://github.com/skywind3000/kcp.git
synced 2025-01-13 20:02:53 +08:00
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…
Reference in New Issue
Block a user