项目学习地址:【牛客网C++服务器项目学习】
day 12
TCP的通信:
1.TCP通信的过程
-
TCP协议作为传输层的协议,承担的任务是实现不同主机之间端-端的通信。在应用层上的协议,如http、ftp协议都是通过TCP协议完成可靠传输的。在我们使用例如,xftp这类软件进行文件传输时,看不见的背后是每一次进行传输,都会在通信的双方之间建立TCP连接,TCP通信的过程大致是这样的:
-
建立连接:三次握手的过程。
-
第一次握手:客户端发起连接。客户端会向服务端送请求连接的TCP报文。请求连接的TCP报文中不包含数据,仅仅是发送连接请求。在这份TCP报文中设置了SYN=1标志位,这样服务端在众多TCP报文中能够识别到这份报文是想建立连接。此外,这份报文中还初始化了一个clien_isn初始序号,用于初始化服务端的TCP报文的ACK序号。(TCP报文由下一层的IP协议承担输运)。
-
第二次握手:假设客户端请求连接的TCP报文顺利地被服务端接收到了之后,如果服务器这边还能继续新建一条TCP连接(一个服务器能够拥有的TCP连接数量是有限的),那么服务器会为这条TCP连接分配一块缓存资源和变量,并向客户端回复一条TCP报文。和第一次握手的请求报文中的信息不一样:首先,报文的SYN=1标记,代表这是同意连接;然后报文中的ACK = client_isn + 1;然后会初始化服务端的server_isn发送给客户端,用于初始化客户端的ACK 序号。
-
第三次握手:假设服务端同意连接的TCP报文顺利地被服务端接收到了之后,客户端经过检查,确认这是我之前发起连接得到的回信。客户端也开始为这次TCP连接分配缓存和变量资源,此外客户端还会给服务器发送一份TCP报文,这份回复报文的意义在于:客户端需要告知服务端,正确接收到了你的回复,现在我们开始通信把。基于此,这份报文中需要包含的信息:SYN置为0,表示连接建立,不用再请求了;ACK序号 = server_isn + 1(序号要对得上才代表这次连接是正确的)。同时,第三次握手可以携带数据,发送给服务端。
-
-
-
进行传输:确认了身份,建立了连接之后的通信双方便可以顺畅的进行通信了。但是网络世界是错综复杂的,建立了连接只是通信的一部分,TCP协议还使用了一些技术手段保证通信的持续、稳定。
- 重传机制:服务器(客户端)迟迟没有回复消息怎么办?这便是重传技术的用途。重传技术发展至今,比较主流的有超时重传、快速重传、SACK、D-SACK
什么时候选择重传?
- 超时重传:通信的双方都有一个定时器,每次发送一条TCP报文后,便开始计时,如果计时器时间走完,还没有收到回复消息,重复发送一次之前的TCP报文。计时器定时的时间通过复杂的计算。如果超时重发的数据,再次超时后,超时时间会加倍。
- 快速重传:超时重传机制的弊端在于重发的效率有点低,反应比较呆。快速重传机制是当接收数据的一方,重复接收一个数据的**ACK**序号回复次数达到**3次**,那么系统就判定此时有数据发送失败或者对方收到不回复,继续重传
重传哪些数据?
* SACK:我们思考一个问题:TCP是怎么保证数据传输的顺序正确的呢?在之前的网络模型总结中,我们知道TCP传输时会对从应用层获取的数据进行分段,同时对每一个分段分配对应顺序的序列号,这个序列号也即TCP报文头部的sequence。对于TCP传输的接收方,每正确接收一个序号对应的数据报,会在内部记录。如果发送方能够知道接收方接收了哪些数据,也就知道哪些数据是没有被正确接收的,重传接收方没有得到的数据即可。
这就是SACK的基本原理,选择性确认。通信的双方在TCP报文头部字段中寻找一个SACK的东西,可以将缓存的地图发送给发送方,这样发送 方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。
- D-SACK在原理上和SACK并无太大区别,D_SACK是将重复接收的数据报告知发送方,目的为了让发送方得知发出去的包是丢了还是接收方回应的ACK丢了。
- 流量控制:
发送方不能无脑的发数据给接收方,要考虑接收方处理能力。 如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪 费。 为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量, 这就是所谓的流量控制
讲到流量控制,那就要先讲一下滑动窗口了
什么是滑动窗口呢?
对于通信的发送方来说,一份待发送的数据,存在四种状态:已发送已确认被接收的、已发送但是未被接收的、未发送的、不可用的。在操作系统中,维护了一个缓冲区窗口用于存放【已发送但是未被接收的】的数据,在发送的过程中,这个缓冲区的数据会不断更新,看起来就像是在待发送的数据段上进行滑动一样,故得名滑动窗口。
滑动窗口机制是在流水线技术上的完善和更新。早期设计的TCP协议只能让发送方将数据分组一个发完等到确认后,再发送新的数据分组。引入流水线技术后大大提高了网络线路的利用率和传输效率。
但是像滑动窗口这样,一次性发送一个窗口的数据,带来了新的问题:这一组的数据如何确定被正确接收?这里TCP协议带来了两个解决方法:GBN(go back N回退N步)、选择重传。
-
断开连接:四次挥手的过程。接收方和发送方都可以主动终止连接。
-
第一次挥手:客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客 户端进入 FIN_WAIT_1 状态。
-
第二次挥手:服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
-
第三次挥手: 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
-
第四次挥手:客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态 。服务器收到了 ACK 应答报文后,就进入了 CLOSED 状态,至此服务端已经完成连接的关闭。 客户端在经过 2MSL 一段时间后,自动进入 CLOSED 状态,至此客户端也完成连接的关闭。
-
2.TCP的头部结构
-
端口号:源端口和目的端口不多讲了。TCP头部的端口号和IP头部的IP地址形成了TCP确定通信双方的四元组
-
序列号:TCP报文中数据的ID标识,序列号是按照数据分割的顺序进行排列的。第一个序列号的生成是随机的,目的是保证不要和之前的旧的,还在网络中飘荡的TCP报文冲突
-
确认应当号:收到数据后给予对方的答复,接收方的ACk和对方的SEQ是关联的
-
首部长度:可选项,一般为空
-
6个标志位:只讲重要的
-
ACK:表示确认应答号是有效的。除了三次握手的第一次握手是0之外,之后的报文都得是1
-
RST:该位为1的话,表示TCP连接出现异常需要中断
-
SYN:请求建立连接。三次握手的报文中会出现这个标志位
-
FIN:拆除连接。四次挥手的报文中会出现这个标志位
-
-
窗口大小:用于流量控制的字段。
-
校验和:用于检查数据在发送过程中是否出现损坏。
3.TCP的重点一:三次握手
4.TCP的重点二:滑动窗口
5.TCP的重点三:四次挥手
未完待续,今天因为私事,学习记录没能整理好。除了自己写TCP的相关知识,我还写了进程的并发socket程序。在研究这个老师说的换行符‘\0’问题,然后我发现read函数也值得深究。待我搞明白这两点后会更新的