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,一经查实,立即删除!

相关文章

执行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…

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

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

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

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

Scrapy 框架【学习笔记01】

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

scrapy startproject【学习笔记02】

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

TotoiseSVN的上手教程

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

【转载】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、前…

TensorFlow训练单特征和多特征的线性回归

线性回归 线性回归是很常见的一种回归&#xff0c;线性回归可以用来预测或者分类&#xff0c;主要解决线性问题。相关知识可看“相关阅读”。 主要思想 在TensorFlow中进行线性回归处理重点是将样本和样本特征矩阵化。 单特征线性回归 单特征回归模型为&#xff1a;ywxb 构建模…

大数据之HDFS应用开发(java API)

目录1、搭建开发环境2、获取api中的客户端对象3、DistributedFileSystem实例对象所具备的方法4、HDFS客户端操作数据代码示例 目录 1、搭建开发环境 window下开发的说明: A、在windows的某个目录下解压一个hadoop的安装包 B、将安装包下的lib和bin目录用对应windows版本平…

shell脚本执行方式,更方便更快捷。

在进行linux测试时编写脚本是必不可少的。最近经常使用Linux&#xff0c;感觉太频繁地敲击键盘有些累了&#xff0c;于是想到了Shell脚本。可以把太多的命令写成一个脚本&#xff0c;这样每次执行一遍 shell文件&#xff0c;就可以省去了敲击键盘的时间。于是在网上搜了一些有…

慕课网_《Java实现对称加密》学习总结

时间&#xff1a;2017年4月11日星期二说明&#xff1a;本文部分内容均来自慕课网。慕课网&#xff1a;http://www.imooc.com教学示例源码&#xff1a;https://github.com/zccodere/s...个人学习源码&#xff1a;https://github.com/zccodere/s... 第一章&#xff1a;对称加密算…

大数据之MapReduce详解(MR的运行机制及配合WordCount实例来说明运行机制)

目录前言&#xff1a;1、MapReduce原理2、mapreduce实践&#xff08;WordCount实例&#xff09; 目录 今天先总体说下MapReduce的相关知识&#xff0c;后续将会详细说明对应的shuffle、mr与yarn的联系、以及mr的join操作的等知识。以下内容全是个人学习后的见解&#xff0c;如…

20155222 2016-2017-2 《Java程序设计》第8周学习总结

20155222 2016-2017-2 《Java程序设计》第8周学习总结 教材学习内容总结 Java NIO(New IO)是一个可以替代标准Java IO API的IO API&#xff08;从Java 1.4开始)&#xff0c;Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers&#xff08;通道和缓冲区&…

BAT经典面试题精简版(基础知识附答案)

文章目录目录J2SE基础JVM操作系统TCP/IP数据结构与算法目录 J2SE基础 九种基本数据类型的大小&#xff0c;以及他们的封装类。 原始类型封装类 booleanBoolean charCharacter byteByte shortShort intInteger longLong floatFloat doubleDouble Switch能否用string做参数&…

使用2to3.py 转换 python2.x 代码 到python3

1.使用Windows 命令提示符&#xff08;cmd&#xff09;cd到2to3.py 脚本所在位置&#xff0c;如下图&#xff1a; 找不到的2 to 3.py的去 pycharm中双击shift搜索一下 2.紧接着运行 2to3.py 脚本&#xff08;可省略&#xff09; 3.执行你想要转换的文件 python 2to3.py -w H:…

iis6.0与asp.net的运行原理

这几天上网翻阅了不少前辈们的关于iis和asp.net运行原理的博客&#xff0c;学的有点零零散散&#xff0c;花了好长时间做了一个小结&#xff08;虽然文字不多&#xff0c;但也花了不少时间呢&#xff09;&#xff0c;鄙人不才&#xff0c;难免有理解不道的地方&#xff0c;还望…

Android学习笔记进阶十之Matrix错切变换

刚开始我也不懂啥叫错切变换&#xff0c;一看效果图你就恍然大悟。 对图像的错切变换做个总结&#xff1a; x x0 b*y0; y d*x0 y0; 与之对应的方法是&#xff1a; [java] view plaincopyMatrix matrix new Matrix(); matrix.setSkew(0.0f, 0.5f);

Django中的核心思想ORM---元类实现ORM

1. ORM是什么 ORM 是 python编程语言后端web框架 Django的核心思想&#xff0c;“Object Relational Mapping”&#xff0c;即对象-关系映射&#xff0c;简称ORM。 一个句话理解就是&#xff1a;创建一个实例对象&#xff0c;用创建它的类名当做数据表名&#xff0c;用创建它…

移动互联网广告 - 第十更 - 广告投放运营 DashBoard - 2016/12/10

广告投放运营 DashBoard设计 移动互联网互联网广告投放&#xff0c;数据监控DashBoard&#xff0c;基础样例示意&#xff0c;下图仅供参考&#xff08;来自于互联网&#xff09;。 转载于:https://www.cnblogs.com/pythonMLer/p/6154700.html