一、KCP协议包
1.1 kcp协议包
kcp中只有一种数据包,不管是数据还是控制信息,都用这个数据包来表示
0 4 5 6 8 (BYTE)
+---------------+---+---+-------+
| conv |cmd|frg| wnd |
+---------------+---+---+-------+ 8
| ts | sn |
+---------------+---------------+ 16
| una | len |
+---------------+---------------+ 24
| |
| DATA (optional) |
| |
+-------------------------------+
-
conv (4 bytes): 会话号,用于区分不同的会话。由于kcp不处理握手,可以通过两端
-
cmd (1 byte): 命令类型,可能的命令类型包括:
-
IKCP_CMD_PUSH
(数据包) -
IKCP_CMD_ACK
(确认包) -
IKCP_CMD_WASK
(窗口探测包,请求对方告知窗口大小) -
IKCP_CMD_WINS
(窗口大小通告包,告知对方窗口大小)
-
-
frg (1 byte): 分片标识,表示数据是否被分片及其序号。对于不分片的数据包,该值为 0。
-
wnd (2 bytes): 窗口大小,用于流量控制。
-
ts (4 bytes): 时间戳,用于计算往返时间(RTT)以及重传超时(RTO)。
-
sn (4 bytes): 序号,用于数据包排序和确认。
-
una (4 bytes): 未确认的序号,用于流量控制。
-
len (4 bytes): 数据部分的长度。
1.2 kcp源码IKCPSEG结构体
//=====================================================================
// SEGMENT
//=====================================================================
struct IKCPSEG
{
struct IQUEUEHEAD node;//链表节点,结构体类型 IQUEUEHEAD,用于把该分段加入到发送或接收队列的链表中。
IUINT32 conv;
IUINT32 cmd;
IUINT32 frg;
IUINT32 wnd;
IUINT32 ts;
IUINT32 sn;
IUINT32 una;
IUINT32 len;
IUINT32 resendts;//重传时间戳,32 位整数,表示该分段下一次进行重传的时间戳。
IUINT32 rto;//重传超时时间,32 位整数,表示该分段的重传超时时间。根据 ACK 的 RTT 值调整。
IUINT32 fastack;//快速重传计数,32 位整数,表示该分段被其他数据包跳过的次数,用于快速重传机制。
IUINT32 xmit;//传输次数,32 位整数,表示该分段已被发送的次数。
char data[1];
};
二、IKCPCB
IKCPCB
结构体是 KCP 协议中控制块的核心数据结构,包含了连接状态、传输参数、缓存和控制信息等。这是整个 KCP 协议运作的核心,用于管理连接的各个方面。下面是对每个字段的详细解释,
看不懂没关系,后边会在流程中解释。
struct IKCPCB {
IUINT32 conv; // 会话 ID,用于区分不同的连接
IUINT32 mtu; // 最大传输单元大小,指网络层能够传输的最大数据包大小
IUINT32 mss; // 最大分段大小,为 MTU 减去固定的 KCP 头部大小
IUINT32 state; // 连接状态
IUINT32 snd_una; // 未确认的发送序号
IUINT32 snd_nxt; // 下一个发送序号
IUINT32 rcv_nxt; // 下一个接收序号
IUINT32 ts_recent; // 最近的时间戳
IUINT32 ts_lastack; // 最后一个确认包的时间戳
IUINT32 ssthresh; // 慢启动门限
IINT32 rx_rttval; // RTT 波动值
IINT32 rx_srtt; // 平滑后的 RTT 值
IINT32 rx_rto; // 重传超时时间
IINT32 rx_minrto; // 最小重传超时时间
IUINT32 ff_recovery_point; // 快速恢复点
IUINT32 snd_wnd; // 发送窗口大小
IUINT32 rcv_wnd; // 接收窗口大小
IUINT32 rmt_wnd; // 远端窗口大小
IUINT32 cwnd; // 拥塞窗口大小
IUINT32 probe; // 探测变量
IUINT32 current; // 当前时间
IUINT32 interval; // 内部工作刷新间隔
IUINT32 ts_flush; // 下次刷新时间戳
IUINT32 xmit; // 传输次数
IUINT32 next_send; // 下一个发送时间
IUINT32 nrcv_buf; // 接收缓冲区大小
IUINT32 nsnd_buf; // 发送缓冲区大小
IUINT32 nrcv_que; // 接收队列大小
IUINT32 nsnd_que; // 发送队列大小
IUINT32 wait_snd_bytes; // 等待发送字节数
IUINT32 wait_rcv_bytes; // 等待接收字节数
IUINT32 nodelay; // 是否启用无延迟模式
IUINT32 updated; // 是否已更新
IUINT32 ts_probe; // 窗口探测时间
IUINT32 probe_wait; // 窗口探测等待时间
IUINT32 dead_link; // 最大重传次数,超过此次数认为连接失效
IUINT32 incr; // 拥塞窗口增加量
IUINT32 ts_wait_ack; // 等待 ACK 的时间戳
IUINT32 flag_wait_ack; // 等待 ACK 的标志
IUINT32 ts_echo; // ECHO 的时间戳
pthread_mutex_t send_lock; // 发送锁,用于多线程环境
struct IQUEUEHEAD snd_queue; // 发送队列
pthread_mutex_t recv_lock; // 接收锁,用于多线程环境
struct IQUEUEHEAD rcv_queue; // 接收队列
struct IQUEUEHEAD snd_buf; // 发送缓冲区
struct IQUEUEHEAD rcv_buf; // 接收缓冲区
IUINT32 *acklist; // 存储待发送的 ACK 列表
IUINT32 ackcount; // ACK 的数量
IUINT32 ts_sack; // SACK 时间戳
IUINT32 ackblock; // ACK 列表的大小(块大小)
void *user; // 用户数据
void *user_send_queue; // 用户发送队列
void *user_recv_queue; // 用户接收队列
char *buffer; // 缓冲区
int fastresend; // 快速重传标志
int nocwnd; // 关闭拥塞控制标志
int stream; // 流模式标志
int logmask; // 日志掩码
int delay_ack; // 延迟 ACK 处理
void *ca_priv; // 拥塞避免私用数据
int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); // 数据输出回调
void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); // 日志写入回调
int (*process_pkt)(void *user, int length, const char *input, char *output); // 数据包处理回调
};