【cdk的使用】C/C++ TCP 粘包 拆包 及解决方案

Github地址: https://github.com/wujin1989/cdk

TCP因为没有边界,所以会有粘包的问题。看下面代码:

server.c

#include "cdk.h"
#include 

#define BUFSIZE    20

void routine(sock_t s) {
	
	int  ret;
	char rbuf[BUFSIZE];

	while (true) {
		memset(rbuf, 0, BUFSIZE);
		ret = recv(s, rbuf, BUFSIZE, MSG_WAITALL);
		if (ret <= 0) {
			abort();
		}
	}
}

int main(void) {
	
	sock_t s;

	s = cdk_tcp_listen("0.0.0.0", "9999");
	cdk_tcp_netpoller(s, routine, false);
	return 0;
}

client.c

#include "cdk.h"

#define BUFSIZE     2

int main(void) {

	int     ret;
	sock_t  c;
	char    sbuf[BUFSIZE];

	c = cdk_tcp_dial("192.168.0.105", "9999");

	for (int i = 0; i < 10; i++) {
	
		cdk_sprintf(sbuf, BUFSIZE, "%d", i);
		ret = send(c, sbuf, BUFSIZE, 0);
		if (ret <= 0) {
			abort();
		}
	}
	while (true);
	return 0;
}

在client端用wireshark抓包如下:
【cdk的使用】C/C++ TCP 粘包 拆包 及解决方案_第1张图片
client.c代码里明明每次发送2个字节数据,发送10次,但从上面抓包来看,tcp合并了client发送的数据包,这个就是TCP发送数据时的粘包。

怎么避免发送时的TCP粘包呢?修改client.c代码,添加TCP_NODELAY选项如下:

#include "cdk.h"

#define BUFSIZE     2

int main(void) {

	int     ret;
	sock_t  c;
	char    sbuf[BUFSIZE];

	c = cdk_tcp_dial("192.168.0.105", "9999");

	for (int i = 0; i < 10; i++) {
	
		cdk_sprintf(sbuf, BUFSIZE, "%d", i);
		cdk_tcp_nodelay(c, true);
		ret = send(c, sbuf, BUFSIZE, 0);
		if (ret <= 0) {
			abort();
		}
	}
	while (true);
	return 0;
}

在client再次抓包,如下:
【cdk的使用】C/C++ TCP 粘包 拆包 及解决方案_第2张图片
看起来,NODELAY选项可以解决发送数据的粘包现象。但是接收数据还是会有可能导致粘包,因为接收端根本不知道每次读多少数据。

接下来看下TCP拆包,TCP的拆包原因是发送数据大于MSS导致的,server还用之前代码,client看下面代码:

client.c

#include "cdk.h"
#include 

#define BUFSIZE     537

int main(void) {

	int     ret;
	sock_t  c;
	char    sbuf[BUFSIZE];

	memset(sbuf, 1, BUFSIZE);
	c = cdk_tcp_dial("192.168.0.105", "9999");

	cdk_tcp_nodelay(c, true);
	ret = send(c, sbuf, BUFSIZE, 0);
	if (ret <= 0) {
		abort();
	}
	
	while (true);
	return 0;
}

上面代码,发送一个537个字节的数据,但是因为cdk内部设置了MSS为536。所以当发送一个大于MSS(536)的数据时,TCP会进行拆包,用wireshark抓包,如下图:
【cdk的使用】C/C++ TCP 粘包 拆包 及解决方案_第3张图片
可以看到,发送数据被分成两个包发送,一个536字节,一个1字节。
这个就是TCP的拆包现象。

如何解决TCP的粘包和拆包呢?

  • 粘包解决方案:
    1. 发送和接收定长数据包。
    2. 发送消息带有分隔符。
    3. 发送消息带头信息,头信息里包含数据的长度。(cdk采用这种方案)
  • 拆包解决方案:
    发送数据不要超过MSS。

具体实现,可以参考cdk。

怎么样?tcp粘包,拆包是不是很容易。快来尝试下吧。

你可能感兴趣的:(cdk,c语言,C++,数据结构,网络)