当前位置: 首页 > news >正文

粘包问题、mmap和分片上传

一、粘包问题:

如果一端要把文件发给另一端,要发送两个部分的数据:其一是文件名,用于对端创建文件;另一个部分是文件内容。服务端在接收文件名,实际上并不知道有多长, 所以它会试图把网络缓冲区的所有内容都读取出来,但是send底层基于的协议是TCP协议 ——这是一种流式协议。这样的情况下,服务端没办法区分到底是哪些部分是文件名而哪些 部分是文件内容。完全可能会出现服务端把文件名和文件内容混杂在一起的情况,这种就是 江湖中所谓的"粘包"问题。

定义一个结构体规定TCP发送和接收的实际长度从而确定单个消息的边界。 

typedef struct train_s{
int size;
char buf[1000];
} train_t;

1、文件比较大时使用循环机制:发送方使用一个循环来读取文件内容,每 当读取一定字节的数据之后,将这些数据的大小和内容填充进小火车当中;接收方就不断的 使用recv接收小火车的火车头和车厢,先读取4个字节的火车头,再根据车厢长度接收后续 内容。

2、 服务端往客户端已经关闭的网络socket中写入数据,导致进程收到SIGPIPE信号异常终止。解决方法是给send的最后一个参数加上MSG_NOSIGNAL选项。

3、调用recv的时 候,需要传入一个整型的长度参数,但是遗憾的是,这个长度参数是描述的是最大的长度, 而实际recv的长度可能并没有达到最大的长度——因为TCP是一种流式协议,它只能负责每 个报文可靠有序地发送和接收,但是并不能保证传输到网络缓冲区当中的就是完整的一个小 火车(即数据到达有延迟)。解决方案就是给recv函数设置MSG_WAITALL属性,这样的话, recv在不遇到EOF或者异常关闭的情况就能一定把最大长度数据读取出来。

二、mmap:

采用read和send传输数据时,首先打开一个普通文件,数据会从磁盘通过DMA设备传输到内存,即文件对象当中的内核缓冲区部分,然后调用read 数据会从内核缓冲区拷贝到一个用户态的buf 上面(buf是 read 函数的参数),接下来调用send,就将数据拷贝到了网络发送缓冲区,最终实现了文件传输。

但这里涉及到了大量不必要的拷贝操作。

 使用mmap系统调用直接建立文件和用户态空间buf的映射。可以减少一次拷贝。

下面是使用mmap的例子:

//假设文件本身的内容是hello#include <func.h>
int main(int argc, char *argv[])
{// ./mmap file1ARGS_CHECK(argc, 2);// 先open文件int fd = open(argv[1], O_RDWR);ERROR_CHECK(fd, -1, "open");// 建立内存和磁盘之间的映射char *p = (char *)mmap(NULL, 5, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);ERROR_CHECK(p, MAP_FAILED, "mmap"); // mmap失败返回不是NULLfor (int i = 0; i < 5; ++i){printf("%c", *(p + i));}printf("\n");*(p + 4) = '0';for (int i = 0; i < 10; ++i){printf("%c", *(p + i));}munmap(p, 5);close(fd);return 0;
}

 运行结果:

mmap的底层原理 :

read/write 是让数据在内核态的文件对象和用户态内存之间进行来回拷 贝,文件对象会和一片由操作系统管理的内存区域(被称为页缓存)相关联,一般来说,操作系统会选 择一个合适策略并使用专门的硬件(比如DMA设备)来同步磁盘和页缓存当中的内容,这样 read/write 操 作最终就会影响到磁盘。而 mmap 的处理就更加简单粗暴,它直接把页缓存的一部分映射到用户态内存, 这样用户在用户态当中的操作就直接对应页缓存的操作。 这样看上去的话, mmap 的效率总是会比 read/write 更加高,因为它避免了一次数据在用户态和内核态 之间的拷贝。但是考虑到 read/write 的特殊性质——它们总是顺序地而不是随机地访问磁盘文件的内 容,所以操作系统可以根据这个特点进行优化,比如文件内容的预读等等,最终经过测试—— read/write 在顺序读写的时候性能更好,而 mmap 在随机访问的时候性能更好。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • spring整合mybatis,junit纯注解开发(包括连接druid报错的所有解决方法)
  • [web]-反序列化-base64
  • 嵌入式C++、STM32、树莓派4B、OpenCV、TensorFlow/Keras深度学习:基于边缘计算的实时异常行为识别
  • 如何使用“Claude Artifact”来生成前端代码
  • 智慧旅游的新引擎:景区客服呼叫中心系统的建设与运营
  • 解决fastjson不输出空字符串、null/设置显示fastjson空值也显示
  • springSecurity学习之springSecurity过滤web请求
  • 基于微信小程序+SpringBoot+Vue的青少年科普教学系统平台(带1w+文档)
  • 【PPT把当前页输出为图片】及【PPT导出图片模糊】的解决方法(sci论文图片清晰度)
  • 深入理解 Java 类加载机制:Arthas classloader 命令解析
  • .NET C# 配置 Options
  • Centos7_Minimal安装Cannot find a valid baseurl for repo: base/7/x86_6
  • 代码随想录第五十九天 | 115.不同的子序列,583. 两个字符串的删除操作, 72. 编辑距离
  • 自学Java第11Day
  • LLM推理优化
  • 2017前端实习生面试总结
  • Angular数据绑定机制
  • bootstrap创建登录注册页面
  • CODING 缺陷管理功能正式开始公测
  • Hibernate最全面试题
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java到底能干嘛?
  • JSDuck 与 AngularJS 融合技巧
  • Meteor的表单提交:Form
  • Promise面试题2实现异步串行执行
  • Rancher如何对接Ceph-RBD块存储
  • REST架构的思考
  • 爱情 北京女病人
  • 大主子表关联的性能优化方法
  • 分类模型——Logistics Regression
  • 如何优雅地使用 Sublime Text
  • 手写一个CommonJS打包工具(一)
  • 用简单代码看卷积组块发展
  • 正则表达式小结
  • 《TCP IP 详解卷1:协议》阅读笔记 - 第六章
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​什么是bug?bug的源头在哪里?
  • #ifdef 的技巧用法
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • $GOPATH/go.mod exists but should not goland
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (Charles)如何抓取手机http的报文
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (Python第六天)文件处理
  • (苍穹外卖)day03菜品管理
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (生成器)yield与(迭代器)generator
  • (使用vite搭建vue3项目(vite + vue3 + vue router + pinia + element plus))
  • (四)Android布局类型(线性布局LinearLayout)
  • (算法)N皇后问题
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)