KCP协议是一个基于UDP的可靠传输协议,在我理解看来就是在UDP的基础上模仿TCP的可靠传输机制进行设计,可以实现快速重传,超时重传等功能。
github上的KCP协议
http://github.com/skywind3000/kcphttp://github.com/skywind3000/kcp
KCP协议提供的代码库可以使得在实现了UDP的基础之上直接套用,也就是在UDP之上直接套用了一层协议,我的理解如下图
整个协议在应用内部就可以实现
以下代码均是C++实现
在实现KCP的时候遇到了一些问题,因此列出来给各位避雷
需要包含的头文件:
#include <WinSock2.h>
#include <stdio.h>
#include "ikcp.c"
#pragma comment(lib,"ws2_32.lib")
1. kcp->output 设置kcp对象的回调函数
可以根据自己的需求设定output函数的需求,一般情况下只需要实现UDP的sendto即可
例如:
kcp->output=udpOutPut;
int udpOutPut(const char* buf, int len, ikcpcb* kcp, void* user) {
//发送信息
int n = sendto(socket, buf, len, 0, (SOCKADDR*)&CientAddr, sizeof(SOCKADDR));
if (n > 0)
{
return n;
}
else
{
return -1;
}
}
2.loop()循环体内的实现形式
由于KCP协议需要使用ikcp_update(ikcpcb* kcp, IUINT32 current)来刷新队列,一般是放在一个循环体内实现
void loop()
{
char buffer[1000];
int hr = 0;
int ServeAddrLen = sizeof(SOCKADDR);
while (1)
{
isleep(1);
ikcp_update(kcp, iclock());//每隔一个时钟进行更新,iclock()是我自己设置的
//检测是否有udp包从p2->p1
while (1) {
hr = recvfrom(socket, buffer, sizeof(buffer), 0, (SOCKADDR*)&addr, &ServeAddrLen);//接收网络中来自p2端的UDP包
if (hr < 0)
{
memset(buffer, NULL, sizeof(buffer));
break;
}
// 如果 p1收到udp,则作为下层协议输入到kcp1
ikcp_input(kcp, buffer, hr);
}
// kcp1收到kcp2的KCP数据包
while (1) {
//输入到kcp1后调用recv读取真正的数据
hr = ikcp_recv(kcp, buffer, sizeof(buffer));
// 没有收到包就退出
if (hr < 0)
{
break;
}
//读取数据
}
}
isleep(10);
}
服务端和客户端的代码是一样的
SOCKET的函数(socket.sendto,socket.recvfrom等)返回值成功是>=0的(大小与存入的buffer大小相同),失败则是-1,如果返回值始终为-1说明出现了问题,我遇到这个问题的原因是由于未调用WSAStartup函数,解决方法就是重定义一下这个函数
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
//return;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
//return;
}
else
{
//return;
}
注意循环体实现过程中可能会堵塞在recvfrom这个阶段,如果通信的两端都堵塞在这个阶段就会造成死锁,这其实是因为SOCKET的函数设置造成的,解决办法有两个,
第一种方法就是将你的loop()函数放入新建的线程之中,在主函数获取调用信息之后再调用线程。
第二种方法就是将SOCKET设置成非阻塞模式
int iMode = 1; //0 :阻塞
ioctlsocket(socket, FIONBIO, (u_long FAR*) & iMode);// 非阻塞设置
3. kcp->send 发送函数
void send()
{
string data;
int ret = ikcp_send(kcp, data.c_str(), data.length());
}
4.可能产生的接收信息异常
在传输字符串的时候可能会出现字符串末尾乱码,解决方法是在字符串末尾加上”0“。但是在传输成功之后别忘了把这个0再去掉。
由于是学生的自习内容,因此必然存在错误,另外由于百度会推送CSDN所以才在CSDN写,实际上CSDN大部分内容都是转载,且帮助作用有限,因此才写下此避雷指南,希望能帮助到需要的人。