Linux 网络编程详解四(流协议与粘包)

TCP/IP协议是一种流协议,流协议是字节流,只有开始和结束,包与包之间没有边界,所以容易产生粘包,但是不会丢包。
UDP/IP协议是数据报,有边界,不存在粘包,但是可能丢包
产生粘包问题的原因
1.SQ_SNDBUF套接字本身有缓冲区(发送缓冲区,接收缓冲区)
2.tcp传送的网络数据最大值MSS大小限制
3.链路层也有MTU(最大传输单元)大小限制,如果数据包大于>MTU要在IP层进行分片,导致消息分割。(可以简单的认为MTU是MSS加包头数据)
4.tcp的流量控制和拥塞控制,也可能导致粘包
5.tacp延迟发送机制等等
结论:TCP/IP协议,在传输层没有处理粘包问题,必须由程序员处理

粘包的解决方案--本质上是要在应用层维护消息与消息的边界
1.定包长
2.包尾加\r\n(比如ftp协议)
3.包头加上包体长度
4.更复杂的应用层协议
粘包的几种状态

 

//粘包解决方案--包头加上包体长度
//服务器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>typedef struct _packet
{int len; //定义包体长度char buf[1024]; //定义包体
} Packet;/** fd:文件描述符* buf:数据缓存区* count:读取字符数* */
ssize_t readn(int fd, const void *buf, ssize_t count)
{//定义临时指针变量char *pbuf = (char *)buf;//定义每次已读数据ssize_t nread = 0;//定义剩余数据ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情况分析:假设b缓冲区buf足够大* 如果nread==count,说明数据正好被读完* nread<count,说明数据没有被读完,这种情况就是由于粘包产生的* socket只接收了数据的一部分,TCP/IP协议不可能出现丢包情况* nread==0,说明对方关闭文件描述符* nread==-1,说明read函数报错* nread>count,这种情况不可能存在* */if (nread == -1){//read()属于可中断睡眠函数,需要做信号处理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已经读取的字节数return count - lread;}//重新获取 剩余的 需要读取的 字节数lread = lread - nread;//指针后移pbuf = pbuf + nread;}return count;
}/* fd:文件描述符* buf:数据缓存区* count:读取字符数* */
ssize_t writen(int fd, const void *buf, ssize_t count)
{//定义临时指针变量char *pbuf = (char *)buf;//每次写入字节数ssize_t nwrite = 0;//剩余未写字节数ssize_t lwrite = count;while (lwrite > 0){nwrite = write(fd, pbuf, lwrite);if (nwrite == -1){if (errno == EINTR)continue;perror("write() err");return -1;} else if (nwrite == 0){printf("client is closed !\n");//对方关闭文件描述符,返回已经写完的字节数return count - lwrite;}lwrite -= nwrite;pbuf += nwrite;}return count;
}int main(int arg, char *args[])
{//create socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);if (listenfd == -1){perror("socket() err");return -1;}//reuseaddrint optval = 1;if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))== -1){perror("setsockopt() err");return -1;}//bindstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)) == -1){perror("bind() err");return -1;}//listenif(listen(listenfd,SOMAXCONN)==-1){perror("listen() err");return -1;}//acceptstruct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn = accept(listenfd, (struct sockaddr *)&peeraddr,&peerlen);if (conn == -1){perror("accept() err");return -1;}Packet _packet;while (1){memset(&_packet, 0, sizeof(_packet));//获取报文自定义包头int rc = readn(conn, &_packet.len, 4);if (rc == -1){exit(0);} else if (rc < 4){exit(0);}//把网络字节序转化成本地字节序int n = ntohl(_packet.len);//获取报文自定义包体rc = readn(conn, _packet.buf, n);if (rc == -1){exit(0);} else if (rc < n){exit(0);}//打印报文数据
        fputs(_packet.buf, stdout);//将原来的报文数据发送回去printf("发送报文的长度%d\n", 4 + n);rc = writen(conn, &_packet, 4 + n);if (rc == -1){exit(0);} else if (rc < 4 + n){exit(0);}}return 0;
}
//粘包解决方案--包头加上包体长度
//客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>typedef struct _packet
{int len; //定义包体长度char buf[1024]; //定义包体
} Packet;/** fd:文件描述符* buf:数据缓存区* count:读取字符数* */
ssize_t readn(int fd, const void *buf, ssize_t count)
{//定义临时指针变量char *pbuf = (char *)buf;//定义每次已读数据ssize_t nread = 0;//定义剩余数据ssize_t lread = count;while (lread > 0){nread = read(fd, pbuf, lread);/** 情况分析:假设b缓冲区buf足够大* 如果nread==count,说明数据正好被读完* nread<count,说明数据没有被读完,这种情况就是由于粘包产生的* socket只接收了数据的一部分,TCP/IP协议不可能出现丢包情况* nread==0,说明对方关闭文件描述符* nread==-1,说明read函数报错* nread>count,这种情况不可能存在* */if (nread == -1){//read()属于可中断睡眠函数,需要做信号处理if (errno == EINTR)continue;perror("read() err");return -1;} else if (nread == 0){printf("client is closed !\n");//返回已经读取的字节数return count - lread;}//重新获取 剩余的 需要读取的 字节数lread = lread - nread;//指针后移pbuf = pbuf + nread;}return count;
}/* fd:文件描述符* buf:数据缓存区* count:读取字符数* */
ssize_t writen(int fd, const void *buf, ssize_t count)
{//定义临时指针变量char *pbuf = (char *)buf;//每次写入字节数ssize_t nwrite = 0;//剩余未写字节数ssize_t lwrite = count;while (lwrite > 0){nwrite = write(fd, pbuf, lwrite);if (nwrite == -1){if (errno == EINTR)continue;perror("write() err");return -1;} else if (nwrite == 0){printf("client is closed !\n");//对方关闭文件描述符,返回已经写完的字节数return count - lwrite;}lwrite -= nwrite;pbuf += nwrite;}return count;
}int main(int arg, char *args[])
{//create socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd == -1){perror("socket() err");return -1;}//connectstruct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = inet_addr("127.0.0.1");if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1){perror("connect() err");return -1;}int rc = 0, numx = 0;Packet _packet;memset(&_packet, 0, sizeof(_packet));while (fgets(_packet.buf, sizeof(_packet.buf), stdin) != NULL){//发送数据numx = strlen(_packet.buf);//将本地字节转化成网络字节序_packet.len = htonl(numx);rc = writen(sockfd, &_packet, 4 + numx);if (rc == -1){return -1;} else if (rc < 4 + numx){return -1;}//接收数据memset(&_packet, 0, sizeof(_packet));//获取包头rc = readn(sockfd, &_packet.len, 4);if (rc == -1){return -1;} else if (rc < 4){return -1;}//将网络字节转化成本地字节numx = ntohl(_packet.len);//printf("接收数据的大小是%d\n",numx);//获取包体rc = readn(sockfd, &_packet.buf, numx);if (rc == -1){return -1;} else if (rc < numx){return -1;}//打印包体
        fputs(_packet.buf,stdout);memset(&_packet, 0, sizeof(_packet));}return 0;
}

 

转载于:https://www.cnblogs.com/zhanggaofeng/p/6134757.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/456782.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决selenium.common.exceptions.WebDriverException: Message: unknown error: call function result missin

(Session info: chrome73.0.3683.103)(Driver info: chromedriver2.30.477700 (0057494ad8732195794a7b32078424f92a5fce41),platformWindows NT 10.0.17134 x86_64)报错如上&#xff0c;由于版本不兼容 下面是谷歌浏览器与chromedriver的版本对应关系&#xff0c;供参考&#…

执行Hive语句报错:FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user '

安装个Hive真不省心&#xff0c;各种问题。最近安装好Hive后执行Hive语句时碰到这样的错误&#xff1a; hive> show databases; FAILED: Error in metadata: javax.jdo.JDOFatalDataStoreException: Access denied for user rootlocalhost (using password: YES) NestedThr…

GPU

import tensorflow as tf a tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[2,3],namea) b tf.constant([1.0,2.0,3.0,4.0,5.0,6.0],shape[3,2],nameb) c tf.matmul(a,b)sess tf.Session(configtf.ConfigProto(log_device_placementTrue)) print sess.run(c)

阿里云部署django项目流程【centos7+python3+mysql】

购买阿里云服务器 到[阿里云官网]&#xff0c;选择轻量应用服务器&#xff0c; 步骤如图所示&#xff1a; 地域随便选择哪一个&#xff0c;镜像的话&#xff0c;对比了CentOS&#xff0c;Debian&#xff0c;Ubuntu&#xff0c;我最终选择了CentOS&#xff0c;因为流行嘛&…

XidianOJ 1123 K=1 Problem of Orz Pandas

题目描述 One panda named orz is playing a interesting game, he gets a big integer Num and an integer K. In this game, he can exchange two single numbers in Num. For example, he can get 1243 from 3241 by exchange 1 and 3.But orz can exchange at most K times…

对于频繁的写数据处理方式

添加一个新的表情的时候 调用 recentEmotions方法 将所有表情写入数组 每次都是 添加一个新的表情进来 要将沙盒中的所有表情首先加载进数组&#xff0c;然后将表情添加到数组里面 然后在将数组写入沙盒 处理方式 没有必要每次都要到沙盒里面读取数组文件 类方法 不能访问 成员…

在Mysql中显示所有用户的操作教程(Linux环境下)

1.登录数据库 首先&#xff0c;你需要使用如下命令登录到数据库&#xff0c;注意&#xff0c;必须是root用户哦~ mysql -u root -p 2.查询用户表 在Mysql中其实有一个内置且名为mysql的数据库&#xff0c;这个数据库中存储的是Mysql的一些数据&#xff0c;比如用户、权限信…

Scrapy 框架【学习笔记01】

Scrapy 框架 Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架&#xff0c;用途非常广泛。 框架的力量&#xff0c;用户只需要定制开发几个模块就可以轻松的实现一个爬虫&#xff0c;用来抓取网页内容以及各种图片&#xff0c;非常之方便。 Scra…

通过profile 用maven命令打不同配置的变量包

profiles定义如下<profiles><profile><id>local</id><properties><deploy.type>local</deploy.type></properties></profile><profile><id>dev</id><properties><deploy.type>dev</de…

执行Hive的查询语句报错:java.lang.IllegalArgumentException: Does not contain a valid host:port authority: loca

好不容易把Hive装完了&#xff0c;结果一执行Hive的查询语句运行MapReduce程序立马报错。。。 log详细信息如下&#xff1a; Job running in-process (local Hadoop) Hadoop job information for null: number of mappers: 1; number of reducers: 0 2017-10-21 21:54:15,503…

scrapy startproject【学习笔记02】

入门案例 学习目标 创建一个Scrapy项目定义提取的结构化数据(Item)编写爬取网站的 Spider 并提取出结构化数据(Item)编写 Item Pipelines 来存储提取到的Item(即结构化数据) 一. 新建项目(scrapy startproject) 在开始爬取之前&#xff0c;必须创建一个新的Scrapy项目。进入…

开始把其他的博客搬家到这里了

今天&#xff0c;用一晚上的时间进行一下文章的迁移吧。这样以后查询就可以在自己的博客中查找了&#xff0c;也算是给自己一个写作的规律。 从很多个大牛的博客中都阅读到&#xff0c;写博客对于一个coder的重要性。希望这次可以坚持。转载于:https://www.cnblogs.com/cyforev…

java系统高并发解决方案(转载)

转载博客地址&#xff1a;http://blog.csdn.net/zxl333/article/details/8685157 一个小型的网站&#xff0c;比如个人网站&#xff0c;可以使用最简单的html静态页面就实现了&#xff0c;配合一些图片达到美化效果&#xff0c;所有的页面均存放在一个目录下&#xff0c;这样的…

Request/Response【学习笔记03】

Request Request 部分源码&#xff1a; # 部分代码 class Request(object_ref):def __init__(self, url, callbackNone, methodGET, headersNone, bodyNone, cookiesNone, metaNone, encodingutf-8, priority0,dont_filterFalse, errbackNone):self._encoding encoding # t…

TotoiseSVN的上手教程

本文转自&#xff1a;http://www.cnblogs.com/xilentz/archive/2010/05/06/1728945.html TotoiseSVN的基本使用方法&#xff1a; 一、签入源代码到SVN服务器 假如我们使用Visual Studio在文件夹StartKit中创建了一个项目&#xff0c;我们要把这个项目的源代码签入到SVN Serv…

ALV可输入状态下输入金额字段变小数的问题

http://blog.163.com/mxb_sapyeah/blog/static/10335262520167109022155/ 小数位数两位 当我在给ALV上给该字段输入整数 ‘1234 ‘ 时&#xff0c;该数据居然会默认变成‘12.34’ 可以在这里解决这个问题。就是定义字段目录的时候&#xff0c;对于金额字段指定参考数据类型就…

Downloader Middlewares反反爬虫【学习笔记04】

反反爬虫相关机制 Some websites implement certain measures to prevent bots from crawling them, with varying degrees of sophistication. Getting around those measures can be difficult and tricky, and may sometimes require special infrastructure. Please consi…

【转载】Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题

转自&#xff1a;【欧阳鹏】http://blog.csdn.net/ouyang_peng Android 设备的CPU类型(通常称为”ABIs”) armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它.arm64-v8a: 第8代、64位ARM处理器&#xff0c;很少设备&#xff0c;三星 G…

HDFS的简介及基本操作(常用的命令参数介绍)

目录前言&#xff1a;1、HDFS基本概念2、HDFS基本操作总结&#xff1a; 目录 前言&#xff1a; 总算有空来接着写大数据的学习笔记了&#xff0c;今天就把之前学过的HDFS的基础知识详细的介绍一下&#xff0c;如有哪点写的不足希望大家多多指教。 1、HDFS基本概念 1.1、前…

Settings【学习笔记05】

Settings Scrapy设置(settings)提供了定制Scrapy组件的方法。可以控制包括核心(core)&#xff0c;插件(extension)&#xff0c;pipeline及spider组件。比如 设置Json Pipeliine、LOG_LEVEL等。 参考文档&#xff1a;http://scrapy-chs.readthedocs.io/zh_CN/1.0/topics/setti…