网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

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

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

TCP 是一种流式协议

在前面的章节中,我们讲的都是单个客户端 - 服务器的例子,可能会给你造成一种错觉,好像 TCP 是一种应答形式的数据传输过程,比如发送端一次发送 network 和 program 这样的报文,在前面的例子中,我们看到的结果基本是这样的:
发送端:network ----> 接收端回应:Hi, network
发送端:program -----> 接收端回应:Hi, program
这其实是一个假象,之所以会这样,是因为网络条件比较好,而且发送的数据也比较少。
为了让大家理解 TCP 数据是流式的这个特性,我们分别从发送端和接收端来阐述。
我们知道,在发送端,当我们调用 send 函数完成数据“发送”以后,数据并没有被真正从网络上发送出去,只是从应用程序拷贝到了操作系统内核协议栈中,至于什么时候真正被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。也就是说,我们不能假设每次 send 调用发送的数据,都会作为一个整体完整地被发送出去。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(29)

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

    作者回复: 非常好的问题。

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

    2019-09-07
    1
    9
  • 徐凯
    第一个是不是跟windows文本换行与linux文本换行的字符不同有关 windows上好像是一个换行符一个回车符 linux是一个换行符
    2019-09-06
    3
  • 刘晓林
    老师,关于网络序和主机序,我有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
    1
    2
  • JasonZhi
    老师,针对粘包问题会有相关的讲解吗?经常会听说相关的名词,但是还是不太懂具体是怎么样的。

    作者回复: 我理解所谓的粘包是数据报文的边界确定不清晰,造成报文解析的时候有overlap,数据解析不对。

    这个具体的解法讲义里也都有涉及到,通过合理设置报文边界,接收端缓冲报文并在解析时注意上下文,一般不会有大的问题。

    2019-09-29
    1
    1
  • wyf2317
    1. windos 和mac linux 的换行不一样。\r 或\r\n
    2. 所属的层级不一样,应用层和网络层
    但本质上就是人为预定的对二进制数据序列化的方式,没有太大的差别,都是通信协议。
    2019-09-08
    1
  • 卫江
    问题1,window与linux平台对于回车换行的编码不一致。
    问题2,协议本质来说就是大家协商好,便于沟通的内容形式。所以,tcp与我们自定义的协议本质来说没有什么不用,区别只是针对的业务不同而已!
    2019-09-06
    1
  • 张立华
    我的操作系统是:centos 7.4 64位操作系统。

    short int = 258;

    258=0x0102

    x的地址是(每次运行地址不一样): 0x7fffffffe33e

    258在内存中:

    低位 高位
    0x7fffffffe33e 0x7fffffffe33f
    00000010 00000001

    也就是说,在我的linux电脑上,内存的数据,是小端字节序

    可以写个简单的程序,用gdb调试下,通过 x命令查看内存

    作者回复: 赞。

    2019-09-06
    1
  • Brave Shine
    1. linux和windows在编码http层面的不同?
    2. tcp的分组报文是传输层在协议栈层面怎么发tcp包的规范,这里指的是应用层报文
    2019-09-06
    1
  • zjstone

    read_line函数用很多次read操作,效率很低,老师应该发个高效率的版本:)
    2019-12-08
  • godtrue
    本文核心观点:
    1:TCP 数据是流式的——0/1的组合——符合规范的直流电——符合规范的交流电
    2:在发送端,当我们调用 send 函数完成数据“发送”以后,数据并没有被真正从网络上发送出去,只是从应用程序拷贝到了操作系统内核协议栈中,至于什么时候真正被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。也就是说,我们不能假设每次 send 调用发送的数据,都会作为一个整体完整地被发送出去。
    3:接收端缓冲区保留了没有被取走的数据,随着应用程序不断从接收端缓冲区读出数据,接收端缓冲区就可以容纳更多新的数据。如果我们使用 recv 从接收端缓冲区读取数据,发送端缓冲区的数据是以字节流的方式存在的,无论发送端如何构造 TCP 分组,接收端最终受到的字节流总是有序的完整的,这些都有TCP严格保证。
    4:数据存储有大小端之别,只要统一就行,网络传输字节序使用大端
    5:都是0/1咋区分数据的边界,常用方式有两种,一是标明字节长度,二是使用特殊分隔符
    2019-11-23
  • 林林
    老师,关于大小端的问题,服务端和客户端互相发包的情况下,为了安全起见,是不是都应该统一进行大小端处理?比如发包都得先转成大端数据,收包再转成机器的顺序?(无论是字符数据还是数值数据)

    作者回复: 是的。实际上就是这样。

    2019-11-04
  • chs
    老师请问这个客户端协议的发送顺序是怎样的?是按照协议里字段的定义顺序发送即先发送数据长度然后发送数据类型最后是发送数据?

    作者回复: 这个对我们来说是透明的,不过你可以理解成先发送数据长度,再发送数据类型,最后是数据本身。

    2019-10-31
  • Steiner
    我想到一个问题,倘若发送端send一个很大的buf,而接收端如果只能接收定长的buf的话就不能一次性处理,这个时候是不是要像文中那样自定义一个结构体包含buf长度和buf来构成一个通信协议来达成一次性处理
    http又是怎么处理这种情况的,他是一次性还是分段呢

    作者回复: 好问题,这个在实际设计应用层协议的,是要着重考虑避免的,就是说边界的划分,应该是有限的,http的协议有head和body的区分,处理的好是可以分段处理的。比如先处理head,再处理body部分。

    2019-10-20
  • Edison.Chen
    老师,我在Linux平台下,照着你的程序写了一遍,客户端发数据,但是好像服务端不能够把数据包给解析出来。

    作者回复: 你写编译一下我这里的例子,看看是否能正确运行。

    你也可以把你的例子贴上来,大家一起分析。

    2019-09-24
    1
  • 甘远林
    大小端问题,如果两边机器的大小端一样,这样两边都不转也没问题吧

    作者回复: 关键是我们不知道对方机器的型号,是不是大小端一样的。

    2019-09-18
  • 有点意思
    老师好
    问题是这样的:
    客户端这边有两个线程:
    一个线程采集网卡流量存到环形缓冲区,另外一个线程从环形缓冲区读取并发送
    格式是:四字节长度+具体内容
    服务端收到后,根据提取到的长度去读取具体内容
    但是在程序运行了一段时间后,发现服务端处理报文出错了,原因是提取的长度是0,
    现在想到的是遇到这种特殊情况,直接断开连接在连,但是总感觉这样处理不好,有点应付差事一样
    我现在有个疑问 出现这种情况是不是一定是服务端拆包的逻辑或者客户端组包的逻辑出了问题 除此之外不会再有其他原因了
    使用原生socket在两台机子间发送接收大数据量的网卡流量 是不是一定能做到可靠没问题?

    作者回复: 是解析完的四字节长度为0,还是读到了0?如果提取的长度为0,说明写入的就是0啊,这个要看下发送端发的数据是否对的。

    2019-09-11
  • Steiner
    这个字节序转换是不是IP地址,端口,数据都要啊

    作者回复: 我其实觉得本地端口不需要,数据是需要的。就是双方通信的那个部分是需要的。

    2019-09-11
  • Steiner
    当我用htonl(999999999)时netstat -tanlp 显示的IP地址是0.0.0.0,数字小一点再试一次是127.0.0.1 当初地址绑定是就是127.0.0.01,这是怎么回事

    作者回复: 感觉是整型数越界了。

    2019-09-11
  • Steiner
    我想问一下,当我把端口设置为htons(2000)时,调用netstat -tanlp 发现端口在2000。当我把端口设置为htonl((uint32_t)2000)时 却是一个随机的整数,每次调用都不一样,这是怎么回事。

    作者回复: 我猜是和int,long的大小有关系,int早期是2个字节,现在一般是4个字节,而long在32位操作系统上是4个字节,64位操作系统是8个字节。

    我不知道你为什么每次调用都不一样,我这里每次都显示一个固定的值。


    2019-09-11
  • yusuf
    第一个问题是因为windows系统下的回车是\r\n,而类UNIX系统下的回车是\n
    2019-09-08
收起评论
29
返回
顶部