基本概念
- TCP本质上是数据流,从原理上看,没有包的概念,TCP包对应用程序员可以是透明的 。
- 粘包实际上是把底层包的实现和上层流的概念混在一起 。
- 粘包问题本质上是如何确定数据流的边界 。
文章插图
通讯之前先通过第三方规定本次发送的包长
- 阻塞发送与接收:
发送:send(fd, wr_data_buf, wr_data_len, 0); /* wr_data_buf 数据缓存,wr_data_len预先设定的固定长度 */接收:recv(fd, wr_data_buf, wr_data_len, 0);
- 如以上代码所示,发送和接收就直接调用socketAPI接口就可以了 。这样写简单,但是有如下问题:容易阻塞主进程,引起多余的进程调度和不可控的系统超时;可以用独立的进程或者线程来优化,但会引起复杂的同步逻辑;无法适应大规模的发送端和接收端同时工作的场景 。
- 无阻塞的发送和接收: 这种方式编码复杂一点,但是解决了阻塞方式引起的问题,是目前的主流解决方案 。发送端的流程图是这样的:Alice 固定长度法发送端流程图说明如下:流程图中假设发送的预设固定长度是1024个字节 。如果利用EPOLL的Level方式,应该在EPOLLOUT的回调函数中调用alice_send_data,隐式实现3->4->3的循环流程 。如果利用EPOLL的Edge方式,应该在EPOLLOUT的回调函数中调用alice_send_epoll,显式实现3-4-3的循环流程 。伪代码是这样的:
static int alice_send_data(int fd, char *wr_data_buf){int n;n = send(fd, wr_data_buf + offset, 1024 - off, MSG_DONTWAIT); /* 无阻塞发送了n个字节*/if (n < 0) {if (errno == EAGAIN || errno == EINTR)return 0;else {return -1; /* error */}} else if (n == 0) {return 1; /* socket close */}offset += n; /*记住总共发了off个字节 */if (off < 1024)/*如果小于预先给定的长度,返回0,继续调用本函数发送 */return 0;return 1; /*发完了,返回1,继续下面的工作 */}static int alice_send_epoll(int fd, char *wr_data_buf) /* edge 方式 */{int offset = 0;int finish;do {finish = alice_send_data(fd, wr_data_buf)} while (!finish);}
【TCP粘包的解决方案】 - 接收端的流程图是这样的:Bob 固定长度法接收端流程图我们可以看出,接收部分可发送部分很相似,这样本文就不重复代码了 。
文章插图
通讯之前先通过第三方规定长度的位置,以便接收端获取
- 发送端知道发送数据的实际长度,然后加上记录长度的4个字节,算出数据总长,按照固定长度的办法发送 。
- 接收端则需要动态获得数据的实际长度,它的流程图是这样的:Bob 变长法接收端流程图
3. 特殊字符串法:在私有和公共协议中实现,比变长法更复杂,但是节省包头长度字段,处理更加灵活 。
文章插图
通讯之前通过第三方规定一个特殊的字符串,比如说'rnrn',接收端才能据此确定数据流的边界 。
- 发送端可以按照固定长度的办法发送 。
- 接收端则需要不断地查找接收缓冲里面的所有数据,看是否有特殊字符串的存在 。它的流程图是这样的:Bob 特殊字符串接收端流程图
推荐阅读
- 茶的十四个秘密功效,茶改变身体的十四项秘密功效
- 茶文化复兴五个进程,聚集少数民族茶文化的地方
- 大禹岭茶泡法,普洱茶的泡法
- 计算机组成:输入/输出接口的端口编址与寻址
- 大红袍的煎煮,大红袍冲泡要点冲泡的水温不低于95℃
- 茶叶的生产地,普洱茶产地的介绍
- 茶的意境乃禅茶悟道,吃茶去茶典故
- 茶叶咖啡因含量,孕妇能喝含咖啡因的茶吗
- 从一个详细的实例来知道单片机编程,你照着做就行了
- 头条为什么会给作者分钱,它的钱是哪里来的?