16 | 如何理解TCP的“流”?
16 | 如何理解TCP的“流”?
讲述:冯永吉
时长11:31大小10.55M
TCP 是一种流式协议
网络字节排序
报文读取和解析
显式编码报文长度
报文格式
发送报文
解析报文:程序
解析报文:readn 函数
解析报文: read_message 函数
实验
特殊字符作为边界
总结
思考题
赞 14
提建议
精选留言(49)
- iron_man2019-09-07一直有个疑问,趁这堂课向老师请教一下,前面客户端发送消息时,消息长度转成网络序了,后面的消息为何没有转成网络序,如果消息里面含有数字呢?如果消息里面全是字符呢?
作者回复: 非常好的问题。 我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据,因为是字符类型的数据,就没有所谓的网络顺序了;而如果作为一个数据型数据,比如125,这时候可能就要作为一个4字节的整型数据进行传输,那么就会有字节序的问题了。
共 3 条评论52 - 徐凯2019-09-06第一个是不是跟windows文本换行与linux文本换行的字符不同有关 windows上好像是一个换行符一个回车符 linux是一个换行符15
- 传说中的成大大2019-09-06message.message_length = htonl( n ); message.message_type = 1; 我在自己写代码实现的时候突然想起这两句代码为什么一个需要htonl一个不需要呢?
作者回复: 我觉得是我错了,应该都需要的,因为我定义的MESSAGE_TYPE是一个int型值。好提醒,能pull一个PR过来么?要不我自己来吧。
共 6 条评论12 - JasonZhi2019-09-29老师,针对粘包问题会有相关的讲解吗?经常会听说相关的名词,但是还是不太懂具体是怎么样的。
作者回复: 我理解所谓的粘包是数据报文的边界确定不清晰,造成报文解析的时候有overlap,数据解析不对。 这个具体的解法讲义里也都有涉及到,通过合理设置报文边界,接收端缓冲报文并在解析时注意上下文,一般不会有大的问题。
共 3 条评论7 - wyf23172019-09-081. windos 和mac linux 的换行不一样。\r 或\r\n 2. 所属的层级不一样,应用层和网络层 但本质上就是人为预定的对二进制数据序列化的方式,没有太大的差别,都是通信协议。5
- J.M.Liu2019-09-07老师,关于网络序和主机序,我有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类型的数据则需要决定顺序。
共 2 条评论6 - 张立华2019-09-06我的操作系统是:centos 7.4 64位操作系统。 short int = 258; 258=0x0102 x的地址是(每次运行地址不一样): 0x7fffffffe33e 258在内存中: 低位 高位 0x7fffffffe33e 0x7fffffffe33f 00000010 00000001 也就是说,在我的linux电脑上,内存的数据,是小端字节序 可以写个简单的程序,用gdb调试下,通过 x命令查看内存展开
作者回复: 赞。
共 2 条评论4 - Richard2020-03-05老师您好,您在第一位留言中有如下回答:“我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据,因为是字符类型的数据,就没有所谓的网络顺序了”。我对此有些疑问,要说现在网络上普遍以UTF-8编码进行传输的话(而UTF-8是单字节码元,因此字节序无关),我能理解您说的“无所谓网络顺序“,但是如果以其他编码方式传输字符呢?所以我有两个问题: 1. 如过通信两端采用UTF-16、UFT-32这些多字节码元编码方式传输是否存在字节序问题? 2.字符集编码是否是socket要考虑的问题?我理解socket只负责传输字节流,编码解码由通信两端完成,不知是否正确?展开
作者回复: 我的意思是,数字可以直接按照二级制进行编码,也可以按照ASCII来进行字符编码,如果是按照字符来进行编码,我认为是没有字节顺序的,只需要把接收到的byte流按照编码格式进行解码即可。 你的理解是对的,编码解码是需要应用程序来完成的。
3 - supermouse2020-02-18思考题第一题:本来想说是因为Unix下的文件的行尾只有\n,而Windows下的文件行尾是\r\n,但是发现老师的代码里考虑的“\r”和“\r\n”这两种情况。所以这一题的答案是考虑到操作系统不同吗? 思考题第二题:区别的话应该是所属层级不同吧,我们自己定义的报文格式是用于应用层,而TCP分组的报文格式是用于传输层;而联系就在于,我们自己定义的报文格式是包含在TCP分组的报文格式中的,即TCP分组报文去掉消息头之后,得到的消息体的格式就是我们自己定义的报文格式展开
作者回复: 对于第一个,确实在服务器端要考虑的,因为你不知道你的客户端是谁。
3 - 卫江2019-09-06问题1,window与linux平台对于回车换行的编码不一致。 问题2,协议本质来说就是大家协商好,便于沟通的内容形式。所以,tcp与我们自定义的协议本质来说没有什么不用,区别只是针对的业务不同而已!3
- 衬衫的价格是19美元2020-06-30为什么需要进行端序转换? 因为数据传输、存储的最小单位是字节, 当我想传输的数据需要一个以上字节才能表示的时候,比如int 类型的 123, 这时接收端收到的是按顺序的四个字节, 他需要知道如何用这四个字节来还原成一个int, 端序转换指定了这个方法, 当然,如果传输的是一个字节就能表示的char类型,就不需要转换了展开
作者回复: 正解。
共 2 条评论3 - xupeng16442020-01-15老师 客户端发送message时 为什么不将messsage_type也转换成网络字节序 而只将message_length转换成网络字节序
作者回复: 我认为你是对的,type类型确实也需要转为网络字节顺序。
2 - zjstone2019-12-08 read_line函数用很多次read操作,效率很低,老师应该发个高效率的版本:)
作者回复: 你有什么更好的思路么?多次read调用在网络程序开发中很正常。
2 - 饭2020-07-02我们在网络传输中,一个常见的方法是把0-9这样的数字,直接用ASCII码作为字符发送出去,在这种情况下,你可以理解成发送出去的都是字符类型的数据, 老师,对这段回答,再加上我们项目现在划分微服务,我一直有疑问。我们服务间现在都是用grpc,而不用基于http的restful服务。因为考虑字符文本传输效率是最低的,体积大。比如本例当中125,如果作为数字传输,不是明显1个字节就可以了,如果用asc要3个字节,如果作为中文unicode,好像6个字节。 而我们传输的 数据对象中,既有字符类型字段(有中文文本),也有数字字段。这种情况下,协议栈是怎么传输的了,整体作为字符传送?展开
作者回复: 这是编码问题,不是网络协议栈的问题。像你说的,可以用UTF-8编码,也可以使用GBK,如果你仔细研究他们,你会发现普遍的现象是,数字、常用字母都是遵循ASCII编码的,也就是8个bit,一个字节就可以搞定,而中文字符一般都是3个字节或以上。
共 2 条评论1 - 张天屹2020-04-02有点疑惑 对于两种方案,不管是数字还是换行符,都有可能在报文的内容中出现,怎么区分是报文正文还是分隔符呢
作者回复: 这就要仔细设置报文了,你没有注意到回车和换行符在http里都是要escape掉么?也就是转义掉,以避免和真正用来做报文分隔的字符冲突了。
1 - 林林2019-11-04老师,关于大小端的问题,服务端和客户端互相发包的情况下,为了安全起见,是不是都应该统一进行大小端处理?比如发包都得先转成大端数据,收包再转成机器的顺序?(无论是字符数据还是数值数据)
作者回复: 是的。实际上就是这样。
1 - Brave Shine2019-09-061. linux和windows在编码http层面的不同? 2. tcp的分组报文是传输层在协议栈层面怎么发tcp包的规范,这里指的是应用层报文1
- 传说中的成大大2019-09-06第1问: 大胆猜测因为http是以回车换行作为分界符但是在程序在编码过程中有可能随时只产生一个回车符 第2问: tcp分组格式 是tcp层面的东西 而本文提到的报文是应用层方面的东西,联系在于tcp层面的分组可能导致数据流出现意想不到的情况,只有通过应用层处理成想要的情况 我记得之前公司解析字节流的时候是以字符0作为结束符,通过字符0 分割出一段数据流 然后通过类型转换 比如( char* ) pbuff转换成字符串或者其他类型的数据,编码的时候也是把数据的编码进去最后添加一个字符0展开
作者回复: 你们这么狠的,用0来作为边界符?0作为字符串截止符是有自己特殊含义的,不过,你们开心就好。
1 - DongGu2022-11-04 来自广东想问一下,是不是发送的数据中,有数值类型的,都要改变大小端之后再发送出去,包括接收1
- Demon.Lee2022-10-29 来自安徽老师,这里讲的报文格式,报文的读取与解析,可以被理解为是序列化与反序列化吗? “将对象的类型、属性类型、属性值一一按照固定的格式写到二进制字节流中来完成序列化,再按照固定的格式一一读出对象的类型、属性类型、属性值,通过这些信息重新创建出一个新的对象,来完成反序列化。”