关于KCP协议的踩雷指南——那些搜索引擎不会告诉你的事情

KCP协议是一个基于UDP的可靠传输协议,在我理解看来就是在UDP的基础上模仿TCP的可靠传输机制进行设计,可以实现快速重传,超时重传等功能。

 github上的KCP协议

http://github.com/skywind3000/kcpicon-default.png?t=LA92http://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大部分内容都是转载,且帮助作用有限,因此才写下此避雷指南,希望能帮助到需要的人。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值