网络编程实战
盛延敏
前大众点评云平台首席架构师
44207 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
网络编程实战
15
15
1.0x
00:00/00:00
登录|注册

16 | 如何理解TCP的“流”?

read_message函数
readn函数
read_line函数
HTTP报文格式
解析报文
发送报文
报文格式
TCP协议栈缓存丢失分组的后续分组
接收端缓冲区保留未读取数据
数据以字节流方式存在
数据发送可能以不同方式组合
数据发送时间取决于多种条件
数据未立即发送
报文格式与TCP分组的区别和联系
HTTP报文格式的处理
特殊字符作为边界
显式编码报文长度
网络字节序转换函数
网络字节序的选择
大端字节序和小端字节序
接收端数据处理
发送端数据处理
思考题
报文读取和解析
网络字节排序
TCP是一种流式协议
TCP数据流特性

该思维导图由 AI 生成,仅供参考

你好,我是盛延敏,这里是网络编程实战第 16 讲,欢迎回来。
上一讲我们讲到了使用 SO_REUSEADDR 套接字选项,可以让服务器满足快速重启的需求。在这一讲里,我们回到数据的收发这个主题,谈一谈如何理解 TCP 的数据流特性。

TCP 是一种流式协议

在前面的章节中,我们讲的都是单个客户端 - 服务器的例子,可能会给你造成一种错觉,好像 TCP 是一种应答形式的数据传输过程,比如发送端一次发送 network 和 program 这样的报文,在前面的例子中,我们看到的结果基本是这样的:
发送端:network ----> 接收端回应:Hi, network
发送端:program -----> 接收端回应:Hi, program
这其实是一个假象,之所以会这样,是因为网络条件比较好,而且发送的数据也比较少。
为了让大家理解 TCP 数据是流式的这个特性,我们分别从发送端和接收端来阐述。
我们知道,在发送端,当我们调用 send 函数完成数据“发送”以后,数据并没有被真正从网络上发送出去,只是从应用程序拷贝到了操作系统内核协议栈中,至于什么时候真正被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。也就是说,我们不能假设每次 send 调用发送的数据,都会作为一个整体完整地被发送出去。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

TCP是一种流式协议,数据发送并非立即完成,而是先拷贝到操作系统内核协议栈中,具体发送时间取决于多种条件。因此,发送的数据不能假设会作为一个完整的整体被发送出去。接收端通过recv从接收端缓冲区读取数据,发送端缓冲区的数据以字节流的方式存在。网络字节序的选择对于网络传输至关重要,必须保证双方使用相同的标准,网络协议使用大端字节序。报文格式定义了字节的组织形式,发送端和接收端都按照统一的报文格式进行数据传输和解析。常见的报文格式有两种方法,一种是发送端把要发送的报文长度预先通过报文告知给接收端,另一种是通过一些特殊的字符来进行边界的划分。文章通过实例展示了发送报文的过程,强调了网络字节序的重要性以及报文格式的定义和解析。TCP数据流特性决定了字节流本身是没有边界的,一般通过显式编码报文长度的方式或选取特殊字符区分报文边界的方式进行报文格式的设计。对报文解析的工作就是在知道报文格式的情况下,有效地对报文信息进行还原。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《网络编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(49)

  • 最新
  • 精选
  • iron_man
    一直有个疑问,趁这堂课向老师请教一下,前面客户端发送消息时,消息长度转成网络序了,后面的消息为何没有转成网络序,如果消息里面含有数字呢?如果消息里面全是字符呢?

    作者回复: 非常好的问题。 我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据,因为是字符类型的数据,就没有所谓的网络顺序了;而如果作为一个数据型数据,比如125,这时候可能就要作为一个4字节的整型数据进行传输,那么就会有字节序的问题了。

    2019-09-07
    3
    56
  • 传说中的成大大
    message.message_length = htonl( n ); message.message_type = 1; 我在自己写代码实现的时候突然想起这两句代码为什么一个需要htonl一个不需要呢?

    作者回复: 我觉得是我错了,应该都需要的,因为我定义的MESSAGE_TYPE是一个int型值。好提醒,能pull一个PR过来么?要不我自己来吧。

    2019-09-06
    6
    12
  • JasonZhi
    老师,针对粘包问题会有相关的讲解吗?经常会听说相关的名词,但是还是不太懂具体是怎么样的。

    作者回复: 我理解所谓的粘包是数据报文的边界确定不清晰,造成报文解析的时候有overlap,数据解析不对。 这个具体的解法讲义里也都有涉及到,通过合理设置报文边界,接收端缓冲报文并在解析时注意上下文,一般不会有大的问题。

    2019-09-29
    4
    8
  • J.M.Liu
    老师,关于网络序和主机序,我有3个问题想要请教一下: 1.在数据发送的时候,是先发送内存中高地址的数据,还是先发内存中低地址的数据?比如char* sendline="abcdef",是从a到f的顺序去发,还是从f到a的顺序去发。 2.接受的时候,是现接受到的是网络序中的高地址,还是低地址? 3.socket接口,如read,send这些函数,会自动帮我们完成主机序和网络序之间的转换吗,还是必须要自己去转?我看老师你有些数据显式调用了htonl(),有些没有,这是为什么呢? 谢谢老师。

    作者回复: 1.如果是字符类型数据,肯定是从a到f这样的顺序拷贝到发送缓冲区发送的; 2.网络中没有高地址或者低地址,网络中传输的是一个字节流,就像你例子里的abcdef....这样的顺序字节流; 3.不会。对于数据型的数据如int需要调研htonl来转换,对于字符类型的数据,不需要转换。因为字符类型的数据,本质是ASCII编码,而int类型的数据则需要决定顺序。

    2019-09-07
    3
    7
  • 衬衫的价格是19美元
    为什么需要进行端序转换? 因为数据传输、存储的最小单位是字节, 当我想传输的数据需要一个以上字节才能表示的时候,比如int 类型的 123, 这时接收端收到的是按顺序的四个字节, 他需要知道如何用这四个字节来还原成一个int, 端序转换指定了这个方法, 当然,如果传输的是一个字节就能表示的char类型,就不需要转换了

    作者回复: 正解。

    2020-06-30
    2
    6
  • 张立华
    我的操作系统是:centos 7.4 64位操作系统。 short int = 258; 258=0x0102 x的地址是(每次运行地址不一样): 0x7fffffffe33e 258在内存中: 低位 高位 0x7fffffffe33e 0x7fffffffe33f 00000010 00000001 也就是说,在我的linux电脑上,内存的数据,是小端字节序 可以写个简单的程序,用gdb调试下,通过 x命令查看内存

    作者回复: 赞。

    2019-09-06
    2
    6
  • 黑客不够黑
    老师您好,您在第一位留言中有如下回答:“我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据,因为是字符类型的数据,就没有所谓的网络顺序了”。我对此有些疑问,要说现在网络上普遍以UTF-8编码进行传输的话(而UTF-8是单字节码元,因此字节序无关),我能理解您说的“无所谓网络顺序“,但是如果以其他编码方式传输字符呢?所以我有两个问题: 1. 如过通信两端采用UTF-16、UFT-32这些多字节码元编码方式传输是否存在字节序问题? 2.字符集编码是否是socket要考虑的问题?我理解socket只负责传输字节流,编码解码由通信两端完成,不知是否正确?

    作者回复: 我的意思是,数字可以直接按照二级制进行编码,也可以按照ASCII来进行字符编码,如果是按照字符来进行编码,我认为是没有字节顺序的,只需要把接收到的byte流按照编码格式进行解码即可。 你的理解是对的,编码解码是需要应用程序来完成的。

    2020-03-05
    5
  • 我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据, 老师,对这段回答,再加上我们项目现在划分微服务,我一直有疑问。我们服务间现在都是用grpc,而不用基于http的restful服务。因为考虑字符文本传输效率是最低的,体积大。比如本例当中125,如果作为数字传输,不是明显1个字节就可以了,如果用asc要3个字节,如果作为中文unicode,好像6个字节。 而我们传输的 数据对象中,既有字符类型字段(有中文文本),也有数字字段。这种情况下,协议栈是怎么传输的了,整体作为字符传送?

    作者回复: 这是编码问题,不是网络协议栈的问题。像你说的,可以用UTF-8编码,也可以使用GBK,如果你仔细研究他们,你会发现普遍的现象是,数字、常用字母都是遵循ASCII编码的,也就是8个bit,一个字节就可以搞定,而中文字符一般都是3个字节或以上。

    2020-07-02
    2
    3
  • supermouse
    思考题第一题:本来想说是因为Unix下的文件的行尾只有\n,而Windows下的文件行尾是\r\n,但是发现老师的代码里考虑的“\r”和“\r\n”这两种情况。所以这一题的答案是考虑到操作系统不同吗? 思考题第二题:区别的话应该是所属层级不同吧,我们自己定义的报文格式是用于应用层,而TCP分组的报文格式是用于传输层;而联系就在于,我们自己定义的报文格式是包含在TCP分组的报文格式中的,即TCP分组报文去掉消息头之后,得到的消息体的格式就是我们自己定义的报文格式

    作者回复: 对于第一个,确实在服务器端要考虑的,因为你不知道你的客户端是谁。

    2020-02-18
    3
  • xupeng1644
    老师 客户端发送message时 为什么不将messsage_type也转换成网络字节序 而只将message_length转换成网络字节序

    作者回复: 我认为你是对的,type类型确实也需要转为网络字节顺序。

    2020-01-15
    3
收起评论
显示
设置
留言
49
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部