目录
首先先了解TCP和UDP协议的特点
TCP(Transmission Control Protocol,传输控制协议)
UDP(User Datagram Protocol,用户数据报协议)
TCP的六个标志位
确认应答
超时重传
连接管理(三次握手,四次挥手)
1.建立连接(三次握手)
2.断开连接(四次挥手)
TCP状态转换中有几个状态需要了解一下
滑动窗口
滑动窗口产生的丢包问题
流量控制
拥塞控制
延时应答
捎带应答
面向字节流
如果解决粘包问题(明确两个包之间的边界)
异常情况
- 首先先了解TCP和UDP协议的特点
TCP(Transmission Control Protocol,传输控制协议)
有连接:
- TCP 是一个面向连接的协议,这意味着在两台计算机之间传输数据之前,它们需要首先建立一个连接。这种连接称为 TCP 连接或套接字(socket)。在建立连接后,两台计算机可以通过这个连接来交换数据,直到连接被关闭。
- 想象你正在给你的朋友打电话。在开始通话之前,你们需要拨对方的号码来建立连接。一旦连接建立,你们就可以开始聊天了。
可靠传输:
- TCP 提供可靠的数据传输服务。它确保发送的数据包(或称为段)能够完整、准确地到达接收方。
- 为了实现可靠性,TCP 使用了一系列的机制,如序列号、确认应答、超时重传、流量控制等。
- 如果数据包在传输过程中丢失或损坏,接收方会向发送方发送一个确认应答(ACK),告知其数据包没有到达或损坏。然后,发送方会重新发送该数据包,直到接收方成功接收到它。
- 就像你在邮寄信件时,你会确保信件被放入信封并正确贴上邮票,然后放入邮筒。你知道邮局会负责将信件送到收件人的手中,即使信件在途中可能会经过多个邮局。
面向字节流:
- TCP 将数据视为一个连续的字节流,而不是单独的数据包。当发送方发送数据时,TCP 会将其分割成适当大小的段,并在接收方重新组装这些段以形成原始的字节流。
- 这意味着 TCP 不保留应用程序发送数据的边界。例如,如果你发送了两个大小为 100 字节的数据包,接收方可能会在一个连续的字节流中一次性读取 200 字节的数据,而不是分别读取两个 100 字节的数据包。
全双工:
- TCP 支持全双工通信,这意味着两台计算机可以同时发送和接收数据。
UDP(User Datagram Protocol,用户数据报协议)
无连接:
- UDP 是一个无连接的协议,这意味着在发送数据之前,发送方和接收方之间不需要建立连接。每个 UDP 数据包(或称为数据报)都是独立的,可以在任何时间、任何顺序到达接收方。
- 就像你正在发送一条短信给你的朋友。你不需要先拨对方的号码来建立连接。你只需编写短信并发送即可。
不可靠传输:
- UDP 不提供可靠的数据传输服务。它不保证数据包能够到达接收方,也不保证数据包的顺序或完整性。
- 如果数据包在传输过程中丢失或损坏,UDP 不会通知发送方,也不会尝试重新发送数据包。
- 这使得 UDP 更适合对实时性要求较高、但对数据准确性要求不高的应用程序,如视频流、实时游戏等。
面向数据报:
- UDP 将数据视为一个个独立的数据报。每个数据报都有自己的头部信息,包括源地址、目的地址、数据长度等。
- 这使得 UDP 能够处理来自不同源地址、不同长度的数据报,并将它们分别发送到不同的目的地址。
全双工:
- 同样地,UDP 也支持全双工通信。
TCP的六个标志位
- SYN(Synchronize):用于建立连接的初始握手。发送方发送一个SYN报文段给接收方,请求建立连接。
- ACK(Acknowledgement):用于确认数据的传输。当成功接收到数据后,接收方发送一个带有ACK标记的报文段回复发送方,确认已经收到了数据。
- FIN(Finish):用于关闭连接。当发送方发送完所有数据后,会发送一个带有FIN标记的报文段,请求关闭连接。接收方在收到FIN报文段后,发送一个带有ACK标记的报文段进行确认,并使用一个定时器在一段时间后关闭连接。
- RST(Reset):用于强制关闭连接。当出现错误或不正常情况时,发送方或接收方可以发送一个带有RST标记的报文段,强制关闭连接,并丢弃已发送或未发送的数据。
- PSH(Push):用于立即传送数据。发送方可以设置PSH标记,通知接收方尽快将数据传送给应用层,而不是等待缓冲区填满或者等待延迟。
- URG(Urgent):用于指定紧急数据。发送方可以设置URG标记,表示报文段中有紧急数据需要尽快处理。接收方收到URG标记后,会立即传送该数据给应用层,并进行相应的处理。
确认应答
TCP确认应答是确保可靠性的最核心机制,主要机制就是,发送方在发完数据后,接收方会返回一个应答报文(ACK),表示已经收到数据,其中每个ACK都带有序列号,意思是告诉发送方我已经收到该序列号以前的全部数据,下次发送数据从该数据以后发送.
并且数据在到达接收方后,会先放到数据缓冲区内,为了防止"后发先至"的情况,会按照序列号排序好
超时重传
是确认应答的补充,一切顺利的话就会有应答报文返回有没有收到数据,这里就要分两种情况讨论
- A--->B发送失败,数据丢包了,A等待特定时间没有收到B返回的ACK就重发数据包给B
- A--->B发送成功,但B返回的ACK丢了,A还会等待特定时间后,重发数据,这时B的缓冲区中加上之前的就会有多份一样的数据包,这里利用序列号进行数据的去重
这里重传还有需要注意的是,如果重发多次都等不到对应的ACK报文返回,重传的频率就会降低,达到一定次数,TCP就会任务网络或者对端主机出现异常,强制关闭连接
连接管理(三次握手,四次挥手)
1.建立连接(三次握手)
- 首先客户端向服务器发送syn(连接请求)
- 服务器接收到syn后,向客户端返回ack,同时服务器向客户端发送syn
- 客户端收到syn后向服务器返回ack
2.断开连接(四次挥手)
- 客户端向服务器发送一个fin(结束报文)
- 服务器接收到fin后,向客户端返回一个ack
- 服务器同时向客户端发送一个fin
- 客户端接收到fin后向服务器返回一个ack
- 三次握手,syn和ack一定可以合起来一起发送 ,因为都是内核完成的
- 四次挥手不一定可以三次完成,因为fin是程序代码控制发送,ack是内核控制,触发时间不一定相同,本身和在一起发送就是为了节省调度消耗,而强制让这两不一定时间的发出的报文放一起,可能得不偿失反而浪费时间
- 建立连接,一定是客户端主动发起的
- 断开连接,客户端服务器都有可能发起
- 建立连接的意义:1.确认当前通信路径是否畅通,2.验证发送和接受能力是否正常,3.通信双方,共同确认一些通信中的必备数据
TCP状态转换中有几个状态需要了解一下
TIME-WAIT
状态:是TCP主动关闭方在发送FIN和ACK后等待一段时间以确保没有迟到的报文段的状态。CLOSE-WAIT
状态:是TCP被动关闭方在收到FIN和发送ACK后等待应用程序关闭连接的状态。如果应用程序没有关闭连接,连接将保持在CLOSE-WAIT
状态,可能导致资源耗尽。- LISTEN状态:是服务器套接字等待客户端连接请求的状态
- ESTABLISHED状态:是TCP连接正常数据传输的状态,表示两台机器之间的通信是活跃的。
滑动窗口
左边是因为可靠传输,引起的效率低,引入滑动窗口,可以提升一点效率,如右图
相比发送一条数据, 收到ACK后发送下一条, 滑动窗口可以一次性发送 N 条数据报,收到 M 条 ACK 的应答后, 窗口向右移动M个位置,并继续发送窗口中没有发送的数据。这样就可以做到将多个 ACK 的等待时间重叠在一起,使用一份时间等待多个ack,总的等待时间缩短了,整体的效率就提升了。批量发送数据:一次发送多条数据,一次等多个ack,称为滑动窗口
- 窗⼝⼤⼩指的是⽆需等待确认应答⽽可以继续发送数据的最⼤值. 上图的窗⼝⼤⼩就是4000个字节 (四个段).
- 发送前四个段的时候, 不需要等待任何ACK, 直接发送;
- 收到第⼀个ACK后, 滑动窗⼝向后移动, 继续发送第五个段的数据; 依次类推;
- 操作系统内核为了维护这个滑动窗⼝, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只
- 有确认应答过的数据, 才能从缓冲区删掉;
- 窗⼝越⼤, 则⽹络的吞吐率就越⾼
滑动窗口产生的丢包问题
- ACK丢了:不要紧,不要重传,因为TCP的确认序号的一意义就是表示该序号之前的数据都收到了,比如2001ACK没有返回,只要大于2001的ACK返回了,就表名2001的数据包已经收到但是如果最后一个丢了就照样超时重传
- 数据包丢了(快速重传升级版):当发送端检测到同一丢失数据包在重复ACK中连续三次被确认(这通常被称为“三重重复ACK”)时,它认为这是一个强烈的指示,表明丢失的数据包已经丢失,并且后续的数据包正在到达接收端。在这种情况下,发送端会假设丢包的可能性很高,并立即重传该丢失的数据包,而不是等待正常的超时期限到期
流量控制
因为滑动窗口越大,传输数据也就越快,但是不能无限大,这里流量控制及时接收方的数据处理能力来调整窗口大小,反过来影响发送方的发送速度,
流量控制的核心
接收端将⾃⼰可以接收的缓冲区⼤⼩放⼊ TCP ⾸部中的 "窗⼝⼤⼩" 字段, 通过ACK端通知发送端; 窗⼝⼤⼩字段越⼤, 说明⽹络的吞吐量越⾼; 接收端⼀旦发现⾃⼰的缓冲区快满了, 就会将窗⼝⼤⼩设置成⼀个更⼩的值通知给发送端; 发送端接受到这个窗⼝之后, 就会减慢⾃⼰的发送速度; 如果接收端缓冲区满了, 就会将窗⼝置为0; 这时发送⽅不再发送数据, 但是需要定期发送⼀个窗⼝探 测数据段, 使接收端把窗⼝⼤⼩告诉发送端
接收方接收缓冲区剩余空间0 ---> 窗口大小设置为0 ---> 发送方不会发送数据
拥塞控制
虽然TCP有了滑动窗口,可以高效的传输大量数据,但是这里如果一开始就发送大量数据仍然可能会引起问题,为解决这一问题,这里TCP引入了慢启动机制,先少量数据传输,检测当前网络拥堵状态,如果正常在指数增长传输的数据量,到达一定慢启动阈值后就开始线性增长,如果丢包了引起超时重发,这里就会调整慢启动阈值,并从新的阈值开始继续线性增长,再不断调整阈值大小,这里线性增长只会出现在一开始
总的来说就是TCP协议想尽快可能把数据传输给对方,但是又要避免给网络造成太大压力的折中方案
延时应答
一般每个发送出去的数据都会返回一个对应ACK,为了提升效率,此时每个几个数据或每个一段时间再返回一个ACK
数量限制:每隔N个包就应答一次
时间限制:超过最大延迟时间就应答一次
这种操作的优点就是:
- 有效传输
- 减少ACK数量,也就是节省开销
捎带应答
捎带应答也就是像三次挥手,中间有一个数据和ACK合并发送给发送端,主要是为了节省开销
面向字节流
当应用程序通过TCP发送数据时,TCP会将数据分割成多个TCP报文段,每个报文段包含一部分数据。这些报文段的长度可能并不相同,取决于多种因素,如网络状况、拥塞情况等。在接收端,TCP会将这些报文段重新组合成原始的字节流,并交付给应用程序
这里就会引出一个粘包问题
意思就是这里的包是应用层的数据包,站在传输层的角度上,TCP是一个一个报文发过来,按照序号拍好放到缓冲区,在应用层角度上看到的只是一串连续字符串,因为TCP也没有像UDP一样的"报文长度"的字段,多个应用层数据混淆不清了,所以应用程序读取字节数据,就会读多,或者读少的问题,也就是我们所谓的粘包问题
如果解决粘包问题(明确两个包之间的边界)
- 对于定长的包,也就是确定了每个包都是一样长的,就可以从缓冲区每次读取该长度,依次读取就好
- 对于变长的包,也就是不确定具体长度的情况下,可以在包头位置约定好包的总长度,也可以在包与包之间使用明确的分隔符(这里程序员自己定就好了,只要分隔符不和正文冲突)
异常情况
- 进程崩溃:进程崩溃也就是进程的PCB没有了,也就是文件描述符表被释放了,也就是相当于调用了socket.close(),也就是崩溃的一方先发出FIN进而触发四次挥手,也就相当于进程被正常释放了。
- 主机关机(正常关机):正常关机会先尝试干掉所有的进程(强制终止进程)和进程崩溃的处理是一样的。
- 主机掉电(拔电源)/网线断开:主机掉电,也就是我们所说的台式机拔电源的情况。
主机掉电可以分成两种情况:
- a)接收方掉电:举个例子,B给A发消息,B发完消息,A没有给返回ACK,此时B就会触发超时重传,若重传仍失败就触发复位报文(RST字符为1)尝试重新连接,若重连操作仍失败,B就会单方面释放连接。
- b)发送方掉电:同样举个例子,A给B发消息,A突然不发消息了,此时的B不知道A是等会就会发消息还是以后一直就不发了,B就会给A发送一个心跳包(也就是窗口探测包)来触发ACK,来判断A是否还在正常工作或者判断网络是否通畅。