前文我们了解 应用层 传输层 网络层 数据链路层 物理层 这五层结构,此文我先讨论传输层相关的知识
1. 传输层
负责数据能够从发送端传输到接收端.
1.1 端口号
端⼝号(Port)标识了⼀个主机上进行通信的不同的应用程序
端口号范围划分:
- 0-1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的
- 1024-65535:操作系统动态分配的端口号.客户端程序的端口号,就是由操作系统从这个范围分配的.
1. ⼀个进程是否可以bind多个端⼝号?
可以2. ⼀个端⼝号是否可以被多个进程bind?
不可以
因为操作系统层面,一个端口对应一个进程,通过端口找到相应的进程
1.2 UDP协议
1.2.1 UDP协议端格式
说明:
- 真实传输的协议端格式是一长条的长方形,此处只是为了方便观看
- 源端口号(16位): 记录发送端的端口号,最长可以用16位(2个字节)来表示
- 目的端口号(16位): 记录接受端的端口号,最长可以用16位(2个字节)来表示
- UDP长度(16位):
表示发送的数据报(UDP⾸部+UDP数据)长度最大为2^16=65535/1024=64kb(个数)
- UDP检验值(16位): 是一种检验机制,利用的是CRC算法(循环去累加byte数组中的值,加出来多少就是多少溢出就溢出了这种校验方式称为CRC,循环冗余校验)
1.2.2 UDP特点
UDP传输的过程类似于寄信.
- 无连接: 知道对端的IP和端口号就直接进行传输,不需要建立连接
- 不可靠: 没有确认机制,没有重传机制,如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息
- 面向数据报: 不能够灵活的控制读写数据的次数和数量
1.2.3 何为面向数据报?
应用层交给UDP多长的报⽂,UDP原样发送,既不会拆分,也不会合并,用UDP传输100个字节的数据,如果发送端调用⼀次sendto,发送100个字节,那么接收端也必须调用对应的⼀次recvfrom,接收100个字节,而不能循环调用10次recvfrom,每次接收10个字节
1.3 TCP协议
TCP全称为"传输控制协议(TransmissionControlProtocol").⼈如其名,要对数据的传输进行⼀个详细的控制
说明:
-
16位源端口号: 记录发送端的端口号,最长可以用16位(2个字节)来表示
-
16位目的端口号: 记录接受端的端口号,最长可以用16位(2个字节)来表示
-
32位序号: 主机A发送数据时,随机生成一个数放在32位序号中
-
32位确认序号: 主机B接受到主机A的随机数后,在随机数的基础上+1后的数放在确认序号中
-
4位首部长度: 表示该TCP头部有多少个32位bit(有多少个4字节),所以TCP头部最大长度是15*4 =60
-
保留(6位): 无作用
-
6位标志位:
URG: 紧急指针是否有效
ACK: 确认号是否有效 (应答标志)
PSH: 提示接收端应用程序⽴刻从TCP缓冲区把数据读⾛
RST: 对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN: 请求建立连接,我们把携带SYN标识的称为同步报文段 (发送标志)
FIN: 通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段 (断开标志) -
16位窗口大小: 后续介绍
-
16位检验和: 发送端填充,CRC校验.接收端校验不通过,则认为数据有问题.此处的检验和不光包含TCP⾸部,也包含TCP数据部分.
-
16位紧急指针: 标识哪部分数据是紧急数据
-
选项:
1.3.1 确认应答
生活中,我们总是会问同学上什么课,假设下列一个场景:
针对上述情况,我们可以对每条消息加上序号,然后回复的消息也与之对应
每⼀个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下⼀次你从哪⾥开始发.
应答和响应的区别?
应答: 表示只是我接受到了数据,是在传输层接收方给发送方的一个回复,只是一个标记,没有真实的结果
响应: 是对我接受到的数据做上处理,然后回应一个处理结果,是应用层根据请求计算出来的结果是真实数据
1.3.2 超时重传
数据在网络上传输的过程中会经过很多网络设备,比如路由器,交换机,运营商,如果其中一个设备出现问题这个请求会出现超时(即规定的时间内没有拿到结果)
情况1:
情况2:
那么,如果超时的时间如何确定?
- 最理想的情况下,找到⼀个最小的时间,保证"确认应答⼀定能在这个时间内返回".
- 但是这个时间的长短,随着网络环境的不同,是有差异的.
- 如果超时时间设的太长,会影响整体的重传效率.
- 如果超时时间设的太短,有可能会频繁发送重复的包.
- TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间.
- Linux中(BSDUnix和Windows也是如此),超时以500ms为⼀个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍.
- 如果重发⼀次之后,仍然得不到应答,等待2*500ms后再进行重传.
- 如果仍然得不到应答,等待4*500ms进行重传.依次类推,以指数形式递增.
- 累计到⼀定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接.
1.3.3 连接管理
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接
作用:在发送方和接收方初次建立连接的时候,确认双方的收发能力
1.3.3.1 握手过程
- 主机A发送SYN请求(SYN置为1),生成一个随机数据填充在32位序号区域中
- 主机B接收到主机A发来的SYN请求后,在序号的基础上+1,把结果填充到确认序号中,并把ACK标志位置为1,表示要进行应答同时也生成一个随机数据填充在序号区域,并把SYN标志位置为1,表示自己发送一个SYN同步请求
- 主机A接收到主机B发来的ACK+SYN请求时,首先判断ACK,表示主机B有应答能力,再去判断SYN,在序号的基础上+1,把结果填充在确认序号中,把ACK标志位置为1
- 主机B接收到主机A发来的ACK,表示主机A有应答能力,网络验证完成,建立连接成功
此时我们尾三次握手,是否可以换成2次握手或者4次握手呢?
两次不行,少验证了主机B的发送与主机A的应答
四次可以,发送与接收方的收发能力都验证通过
1.3.3.1 挥手过程
作用: 保证发送与接收方有效(安全)的断开连接
四次挥手能不能变成三次挥手? 第二步的ACK能不能与第三步的FIN合并在一起?
大概率不能合并在一起,因为
1.发起的角色不同,一个是操作系统,一个是应用程序
2.发起的进机不同,ACK是应答比较及时,FIN要回收一些资源之后才触发
1.3.4 滑动窗口(效率机制)
刚才我们讨论了确认应答策略,对每⼀个发送的数据段,都要给⼀个ACK确认应答.收到ACK后再发送下⼀个数据段.这样做有⼀个比较大的缺点,就是性能较差.尤其是数据往返的时间较长的时候.
既然这样⼀发⼀收的方式性能较低,那么我们⼀次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在⼀起了).这就是滑动窗口带来的效果
说明:
- 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值.
上图的窗口大小就是4000个字节(四个段). - 发送前四个段的时候,不需要等待任何ACK,直接发送
- 收到第⼀个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推
- 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有确认应答过的数据,才能从缓冲区删掉
- 窗口越大,则网络的吞吐率就越高
发送流程:
- 发送方批量发送,把正在发送的数据加入到缓冲区,同时记录最大字节数
- 根据当前窗口的大小发送报文
- 接收方收到报文之后,返回一个ACK(确认序号)
- 发送方接收到一个ACK之后,把缓冲区的数据删除一组,然后再加入一组新的数据到缓冲区,继续发送
那么如果出现了丢包,如何进⾏重传?这里分两种情况讨论.
情况一:数据包已经抵达,ACK被丢了
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认
说明: 图中,2001/5001/6001这些ACK是正常到达了主机A,TCP传输数据的过程中,如果收到了6001的确认序号,那么就可以证明,前面的全部都传输成功
例如:别人问你什么学历?你说你是大学本科,就证明你拥有了小学初中的知识情况二:数据包就直接丢了
说明:
- 当某⼀段报文段丢失之后,发送端会⼀直收到1001这样的ACK,就像是在提醒发送端"我想要的是1001" ⼀样
- 如果发送端主机连续三次收到了同样⼀个"1001"这样的应答,就会将对应的数据1001-2000重新发送
- 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2001-7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中
这种机制被称为"高速重发控制"(也叫"快重传").
1.3.5 流量控制
- 接收端处理数据的速度是有限的.如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起丢包重传等等⼀系列连锁反应.
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度.这个机制就叫做流量控制(FlowControl) - 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端
- 窗口大小字段越大,说明网络的吞吐量越高
- 接收端⼀旦发现自己的缓冲区快满了,就会将窗口大小设置成⼀个更小的值通知给发送端
- 发送端接受到这个窗口之后,就会减慢自己的发送速度
作用: 用来控制发送方的窗口大小,通过接收方返回来的ACK进行反制(接收方把自己能够处理的数据量主动告诉发送方从而让发送方动态调整窗口大小)
说明:
1.具体实现过程是怎样的??2.最后双方停止了通讯后,后续该如何建立连接呢?
此时主机A就发送一个窗口探测,窗口探测不携带具体的数据,只是问一下接收方,现在可以处理数据了不,能处理多少
3.数据最大处理多少??
实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实实际窗口大小是窗口字段的值左移M位
1.3.6 拥塞控制
虽然TCP有了滑动窗口这个大杀器,能够高效可靠的发送大量的数据.但是如果在刚开始阶段就发送大量的数据,仍然可能引发问题.
因为网络上有很多的计算机,可能当前的网络状态就已经比较拥堵.在不清楚当前网络状态下,贸然发送大量的数据,是很有可能引起雪上加霜的.
TCP引入慢启动机制,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据
作用: 通过网络的畅通程度来控制窗口大小
说明:
- 程序启动的时候把拥塞窗口的值调到很少,比如1,
- 如果接收到ACK表示当前窗口大小没有问题,然后就调大窗口大小
- 到达阈值之前以指数形式增大,到达阈值之后,以1为步长进行增大
- 当增大到一定程序发生丢包的情况,就证明网络阻塞了,这时就把窗口大小重新调用1,同时重置新的阈值=当前窗口大小的二分之一
- 重复1~4步
1.3.7 延迟应答(效率机制)
说明: TCP在应答时,并不是每收到一个请求应答一次而是每隔几个应答一次,可能是2
如果请求一共只有三条报文就结束了,还可以通过时间间隔进行ACK,一般是200ms
1.3.8 捎带应答
- 在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是"⼀发⼀收"的.
- 意味着主机A给主机B说了"How are you",主机B也会给主机A回⼀个"Fine,thank you",
- 那么这个时候ACK就可以搭顺风车,和主机B回应的"Fine,thankyou"⼀起回给主机A
- 由于我们知道TCP协议是全双工的
- ACK应答是系统操作是基于传输层的
- 响应是应用层做出的,当给主机A发送响应数据时
- 如果有ACK需要返回,那么这两个报文就有可能被合并成一,减少了通信次数,提升了效率,最好的捎带应答的例子就是三次握手中SYN+ACK
1.3.9 面对字节流
创建⼀个TCP的socket,同时在内核中创建⼀个发送缓冲区(写)和⼀个接收缓冲区(读)
- 调用write时,数据会先写入发送缓冲区中
- 如果发送的字节数太长,会被拆分成多个TCP的数据包发出
- 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去
- 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区
- 然后应用程序可以调用read从接收缓冲区拿数据
- 另一方⾯,TCP的⼀个连接,既有发送缓冲区,也有接收缓冲区,那么对于这⼀个连接,既可以读数据,也可以写数据.这个概念叫做全双工
由于缓冲区的存在,TCP程序的读和写不需要⼀⼀匹配,例如:
- 写100个字节数据时,可以调用⼀次write写100个字节,也可以调用100次write,每次写⼀个字节
- 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以⼀次read100个字节,也可以⼀次read⼀个字节,重复100次
1.3.10 粘包问题
1. 什么是粘包问题呢???
- 首先要明确,粘包问题中的 “包” ,是指的应用层的数据包.
- 在TCP的协议头中,没有如同UDP⼀样的"报文长度"这样的字段,但是有⼀个序号这样的字段.
- 站在传输层的角度,TCP是⼀个⼀个报文过来的.按照序号排好序放在缓冲区中.
- 站在应用层的角度,看到的只是⼀串连续的字节数据.
- 那么应用程序看到了这么⼀连串的字节数据,就不知道从哪个部分开始到哪个部分,是⼀个完整的应用层数据包.
2.如何解决粘包问题呢???
①明确两个包的界限,为每个消息定义一个分隔符,或者说用一个分隔符来界边一条消息\r\n
我想你了,你想我吗 \r\n 在不在呀 \r\n是不是在忙 \r\n 啦啦啦啦啦啦啦 \r\n
②在应用层协议中定义一个区域(字段),用来表示当前消息的长度
1.3.11 异常情况
- 程序崩溃
系统会回收进程的资源,包括文件描述符表,回收时相当于调用socket的close0,触发FIN操作(四次挥手,正常断开) - 正常关机
处理方式和程序崩溃一样,都是正常断开连接 - 主机掉电或断网(工作中的常见问题)
1.接收方断网
发送方收不到ACK应答,此时触发发送方进行超时重传,多次重传依然没有收到ACK时,会进行重置连接,若连接重置也失败,只能放弃连接,将6个标志位中的RST标志位置为1
2.发送方断网
举例:共享单车系统中,每一辆车都要和服务器保持长连接,那么服务器如何知道车是否在线?
此时每台车每隔一分钟向服务器发一个报文,如果服务器收到则表示当前这个车是一直在线的,服务器定期扫描管理的连接,如果发现一个连接长时间没有发来心跳就主动丢弃这个连接