目录
前言
在前面套接字的讲解中我们已经接触到了UDP/TCP它们各自的使用方法,而本次我们来深入了解它们背后蕴含的原理~
传输层协议UDP
再谈端口号

ip标识互联网中唯一主机,而端口号则帮助我们寻找对应的服务进程~

UDP协议端格式

- 16位UDP长度,表示整个数据报(UDP首部+UDP数据)的最大长度;
- 如果校验和出错,就会直接丢弃;
理解为报头+有效载荷即可~
UDP的特点
- 无连接:知道对端的IP和端口号就直接进行传输,不需要建立连接;
- 不可靠:没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息;
- 面向数据报:不能够灵活的控制读写数据的次数和数量;
其实不可靠也是可利用的一点,这意味着其成分简单容易实现~
面向数据报
UDP的缓冲区
- UDP没有真正意义上的发送缓冲区.调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续的传输动作;
- UDP具有接收缓冲区.但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致;如果缓冲区满了,再到达的UDP数据就会被丢弃;
———————————————————————————————————————————
最后提出一个问题:在OS内一定会堆积大量暂时未处理的报文,那么我们应如何对这些报文进行管理呢? ——先描述,后组织~

描述报文内部属性,再用数据结构组织起来~

封装:当经过信息经过传输层时就要添上传输层的报头,并通过移动指针填写报头内容~
解包:先提取出内容(udphdr*)head->srcport... 再移动指针head+=sizeof(udphdr)
_____________________________________________________________________________
UPD说实话上面的都是一些喜闻乐见的简单概念吧,而接下来的TCP就有些难度了~
传输层协议TCP
TCP协议段格式

先来讲讲首部长度,它是包含了报头+选项的大小~范围在[0,15]基本单位4字节。即[0,60]字节内,由于报头固定长度为20字节,那么选项的可变范围就是【0,40】字节了。
假设首部长度为20字节,那么换算单位后就是x*4 = 20,x = 5==》0101~
确认应答(ACK)机制
前面了解过TCP要保证可靠性,即保证发出的信息要被应答,这样才能说明对方成功收到了信息~

最开始的应答机制仅仅是为了回应发送端,而这是双方OS都会自动去完成的。但这样有一个弊端,客户端必须得等待应答才可以继续发送,服务端也一样,大大影响通信效率~
后续版本中引入了序号以实现同时发送多条信息~

发送多条信息的时候携带各自的序号,服务端在接收消息作应答时会填入确认序号(在其序号基础上+1),并且客户端在接收应答消息时是根据它们的序号进行排序的~而不是根据处理信息的时间长短~
ps:关于为什么报头要携带序号和确认序号的原因是因为服务端不仅仅要应答,也是要发送信息的~它发送信息肯定也要有它自己的序列号,也要有能让别人应答的确认序号。
另外要稍微补充的一个知识点:发送数据可不仅仅是“你好”这样明文式发送,而是带有协议段格式的结构体类型发送出去的~

———————————————————————————————————————————
当有一方既想要做确认应答,又需要发数据的时候二者可以合二为一变成捎带应答~

序号:按序到达,应答和确认对应
确认序号&&序号:同时向对方发数据的同时也在做应答。
TCP标志位
- URG: 紧急指针是否有效
- ACK: 确认号是否有效
- PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走
- RST: 对方要求重新建立连接; 我们把携带 RST 标识的称为复位报文段
- SYN: 请求建立连接; 我们把携带 SYN 标识的称为同步报文段
- FIN: 通知对方, 本端要关闭了, 我们称携带 FIN 标识的为结束报文段
为什么TCP要有标记位呢?其存在的意义是什么?


而标记位置1的这些操作都是由OS自主完成的~
超时重传机制
举例以下两种情况:


这种情况会堆积大量重复的数据,不过序号本身有去重的效果所以不用担心。
因为没有收到对方报文而超时的情况,找到一个最小的时间,保证"确认应答一定能在这个时间内返回"TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间.
连接管理机制

在学习之前得先确认一点:三次握手不是一定确保能够建立好连接,而是经历完三次握手,CS双方都认为连接建立好了!
这里举例一下可能失败的场景:

当ACK丢失时,客户端发出即认为连接建立成功,因为它没有应答只能默认发出即成功,而服务端这边因为收不到应答是无法建立连接的,CS双方认知产生差异,最后只能靠超时重传来解决问题~
最后由服务端发给客户端一个携带RST标记位的报文,让双方重新进行三次握手~
为什么要三次握手:
需要确保信道(网络)是健康的,三次握手使得双发都会有确定的一次收发,用来确认其全双工机制
确保双方OS是健康且愿意通信的,因为这几次携带标记位的报文中都是OS自主完成的。
ps:其实也可以理解为4次握手,明显中间的SYN+ACK是捎带应答~
为什么要四次挥手的原理跟上面相同~
最后是关于各个状态码的解释:

[CLOSED->LISTEN]服务器端调用listen后进入LISTEN状态,等待客户端连接;[LISTEN->SYN_RCVD]一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文.[SYN_RCVD->ESTABLISHED]服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了.[ESTABLISHED->CLOSE_WAIT]当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;[CLOSE_WAIT->LAST_ACK]进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)[LAST_ACK->CLOSED]服务器收到了对FIN的ACK,彻底关闭连接.
[CLOSED->SYN_SENT]客户端调用connect,发送同步报文段;[SYN_SENT->ESTABLISHED]connect调用成功,则进入ESTABLISHED状态,开始读写数据;[ESTABLISHED->FIN_WAIT_1]客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1;[FIN_WAIT_1->FIN_WAIT_2]客户端收到服务器对结束报文段的确认,则进入FINWAIT2,开始等待服务器的结束报文段;[FIN_WAIT_2->TIME_WAIT]客户端收到服务器发来的结束报文段,进入TIMEWAIT,并发出LAST_ACK;[TIME_WAIT->CLOSED]客户端要等待一个2MSL(MaxSegmentLife,报文最大生存时间)的时间,才会进入CLOSED状态
理解TIME_WAIT状态
MSL 是 TCP 报文的最大生存时间 , 因此 TIME_WAIT 持续存在 2MSL 的话 就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失 (否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据 , 但是这种数据很可能是错 误的);同时也是在理论上保证最后一个报文可靠到达 ( 假设最后一个 ACK 丢失 ,那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了 , 但是 TCP 连接还在 , 仍然可以重发LAST_ACK);反正核心就是确保之前被堵塞的报文在重启服务器前能完全消失,另一个就是预防ACK丢失时有重新发送的时间。
理解CLOSE_WAIT状态
流量控制
- 接收端将自己可以接收的缓冲区大小放入TCP首部中的"窗口大小"字段,通过ACK端通知发送端;
- 窗口大小字段越大,说明网络的吞吐量越高;
- 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端;
- 发送端接受到这个窗口之后,就会减慢自己的发送速度;
- 如果接收端缓冲区满了,就会将窗口置为0;这时发送方不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端.
滑动窗口
前面场景中这种串行方式效率低下
当然在不考虑网络的情况下,滑动窗口的大小一般是:对方缓冲区中剩余的空间大小,一定是动态变化的,不是像图中保持4000字节大小并且其数据都是无需等待确认应答就可以发送的!

如果遇到超时重传时,滑动窗口会对没有收到应答的报文继续保存,不移动即可直到收到应答才进行滑动。

要建立一个共识,滑动窗口是会随着缓冲区剩余空间大小而为之变化的。

以上是滑动窗口的基本变化流程~
接下来我们来分析一下在丢包情况下滑动窗口的应对情况:

这里我们需要重谈确认序号的意义:确认序号之前的报文全部收到了!
这代表即使我们没有收到1001-2000的应答(应答报文丢失下),但只要它收到了2001-3000的应答,那就一定代表1-3000的发送数据全收到了,因为这就是确认序号的意义所在,它不会做它无法确认的事情。

那么如果是发送报文1001-2000丢失呢?即使后续的发送报文收到了也绝对不敢发送其确认应答,因为从确认序号这里就无法保证,并没有实际收到1001-2000的发送报文。它只敢发送1001的确认序号~

当我们连续发送多个报文出现报文丢失时,会触发高速重发控制(也叫"快重传")机制,发送端主机连续三次收到了同样一个"1001"这样的应答,就会将对应的数据1001-2000重新发送;
三个以下的话就正常超时重传。

所以这里看似三种情况,其实都可以归结于第一种,不管怎样最后都是利用快重传或超时重传一个个补发数据,当然核心仍在于确认序号的意义非凡。
拥塞控制

拥塞就是在考虑网络的情况下对滑动窗口进行限制:
滑动窗口 = min(接收方的窗口(接收能力),拥塞窗口)。在网络堵塞情况下,接收方能力再大也得看拥塞窗口,因为其保证数据在网络通道能否顺利传递。同样的在网络良好的情况下也得看接收方能力,网络再好也得看其接收程度。二者共同决定了滑动窗口的大小~
所以后面引入了更合理的拥塞窗口变化方案:
- 当TCP开始启动的时候,慢启动阈值等于窗口最大值;
- 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1;
- 少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞;
- 当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案.
延迟应答
面向字节流
粘包问题
TCP异常情况
- 进程终止:进程终止会释放文件描述符,仍然可以发送FIN.和正常关闭没有什么区别.
- 机器重启:和进程终止的情况相同.
- 机器掉电/网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行reset.即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在.如果对方不在,也会把连接释放.

1172

被折叠的 条评论
为什么被折叠?



