1. TCP三次握手
- 第一次握手(SYN):
- 客户端发送SYN:客户端选择一个初始序列号seq=x,发送SYN包(包含序列号seq=x)给服务器。表示客户端请求建立连接。
- 客户端状态:客户端进入SYN_SEND状态,等待服务器响应。
- 第二次握手(SYN-ACK):
- 服务器接收到并响应SYN:服务器收到客户端的SYN包后,选择一个初始序列号seq=y,并发送一个SYN-ACK包(包含服务器的序列号seq=y、对客户端的确认序列号ack=x+1)给客户端。
- 服务器状态:服务器进入SYN_RECEIVED状态,等待客户端确认。
- 第三次握手(ACK):
- 客户端确认连接:客户端收到服务器的SYN-ACK包后,发送一个ACK包(包含客户端的序列号seq=x+1、对服务器的确认序列号ack=y+1)给服务器。表示客户端已接收到了服务器的SYN包。
- 双方状态:都进入ESTABLISHED状态,表示连接成功,可以进行数据传输。
1.1 三次握手中如果数据发送失败,如何设计处理?
- 设计重试机制:
- 在客户端发送一个数据包后,在设定的超时时间内未收到服务端的确认应答包。客户端立刻重发数据包。
- 重试次数设定一个上限,若超过上限,则认定服务端已经断联。
- 设计超时机制:
- 设定一个合理的超时时间,例如2秒,客户端在发送SYN包后等待这个时间,若超过这个时间立刻重发数据包。
1.2 第一次握手可以传输数据吗?
- 不可以传输数据,原因是:
- 连接未建立,服务器还未确认客户端的连接请求。客户端无法确认服务器是否收到SYN包,所以无法保证数据传输是可靠的。一次握手就传输数据可能导致旧的连接请求被误认为新的连接,造成数据传输混乱。
1.3 为什么要三次握手?不能两次吗?
原因: 三次握手确保客户端和服务端都知道对方的接收、发送能力是正常的。避免旧的历史连接请求被误认为是新的连接请求。
- 避免历史连接请求的影响:若使用两次握手,旧的连接请求(可能因网络延迟而滞留的SYN包)被误认为是新的连接请求,导致连接状态不一致。
- 双方确认机制不足:两次握手无法保证双方确认了连接的存在。(第一次握手, 接收方知道发送方的发送能力正常。第二次握手 ,发送方知道接收方的发送、接收能力正常。第三次握手 ,接收方知道发送方的接收能力正常)
2. TCP四次挥手
- 第一次挥手(FIN):
- 客户端发送FIN:客户端选择一个序列号seq=x,发送FIN包给服务器。表示客户端已经没有数据要发送,准备关闭连接。
- 客户端状态:客户端进入FIN_WAIT_1状态,等待服务器响应。
- 第二次挥手(ACK):
- 服务器发送ACK:服务器收到FIN包后,发送一个ACK包(包含服务器的序列号seq=y、对客户端的确认序列号ack=x+1)。表示已经接收到客户端的关闭请求。
- 服务器状态:服务器进入CLOSE_WAIT状态,表示服务器仍可能有数据要发送,发送完后发送FIN包。
- 客户端状态:客户端进入FIN_WAIT_2状态,等待服务器响应关闭请求。
- 第三次挥手(FIN):
- 服务器发送FIN:服务器完成最后的数据发送后,发送FIN包(包含服务器的序列号seq=y、对客户端的确认序列号ack=x+1)和ACK报文。表示服务器也准备关闭连接。
- 服务器状态:服务器进入LAST_ACK状态。等待客户端确认。
- 第四次挥手(ACK):
- 客户端发送ACK:客户端收到FIN包后,发送ACK包(包含客户端的序列号seq=x+1、对服务器的确认序列号ack=y+1)。表示确认关闭连接。
- 客户端状态:客户端进入TIME_WAIT状态,等待2MSL(最大报文生存时间)后关闭连接进入CLOSED状态。
- 服务器状态:服务器收到ACK包后,进入CLOSED状态。
2.1 挥手时的 CLOSE_WAIT阶段还能收到报文吗?
- CLOSE_WAIT状态:表示一方已收到对方的FIN包,并发送了ACK包,等待自身处理好未发送的包后关闭连接。在CLOSE_WAIT状态仍然可以接收数据,直到接收方的状态为CLOSED停止接收。
2.2 为什么连接需要三次握手?关闭需要四次挥手?
- 三次握手的原因:
- 防止重复连接:通过三次握手可以让双方都确认对方准备好了,避免历史连接请求影响当前连接。
- 保证双方都可发送和接收:保证双方都能确认对方的接收和发送能力是正常的。
- 四次挥手的原因:
- 半关闭状态:TCP连接是全双工的,双方都可独立关闭自己的发送和接收通道。四次挥手可以实现这种半关闭状态,双方都能完成各自的数据传输。
- 可靠释放资源:通过四次挥手,确保双方都能可靠的释放掉连接资源,避免重复释放或一方释放,另一方还在连接状态无法释放。
2.3 为什么客户端的TIME_WAIT状态必须要等待2MSL?
- MSL:是一个TCP报文段在网络中可以存活的最长时间。
- 必须要等待2MSL的原因:
1)确保被动关闭方收到ACK:
- 当主动关闭方发送最后一个ACK报文时,ACK报文段可能会丢失。被动关闭方没有收到这个ACK报文,就会重发FIN报文。
- 主动关闭方必须等待一段时间,以确保他能收到可能重发的FIN报文,并再次发送ACK报文。
- 如果没有等待2MSL,主动关闭方可能会在被动方发送FIN报文前关闭,导致被动关闭方无法关闭连接。
2)确保旧的报文段在网络中消失:
- 在2MSL时间内,网络中所有旧的、重复的报文都超时被弃用。确保下一次新的连接不会收到前一个连接的旧报文段,避免数据混乱和连接错误。
3. TCP和UDP的头部
- TCP头部:
- 源端口:发送方的端口号。
- 目的端口:接收方的端口号。
- 序列号:用于标识发送的数据字节流的顺序。
- 确认应答号:用于确认收到的数据字节的下一个序列号。
- 数据偏移:表示TCP的头部。
- 保留:保留位,未使用。
- 控制标志:包括URG、ACK、PSH、PST、SYN、FIN六个控制位。
- 窗口大小:接收窗口的大小,用于流量控制。
- 校验和:用于校验头部和数据部分的完整性。
- 紧急指针:指示紧急数据的结束位置。
- 选项:可选字段,长度可变。
- UDP头部:
- 源端口:发送方的端口号。
- 目的端口:接收方的端口号。
- 长度:UDP头部和数据的总长度,以字节为单位。
- 校验和:用于校验头部和数据部分的完整性。
4.TCP可靠性及相关问题
4.1 TCP流量控制
定义:TCP的流量控制是通过滑动窗口实现的,确保发送方不会淹没接收方的处理能力。滑动窗口机制由发送窗口和接收窗口控制。
- 发送窗口 :发送方维护的流量控制边界,用于限制 "已发送但未收到确认" 的数据总量。它的范围由两部分决定:
- 已发送但未被接收方确认的数据。
- 接收方通过 TCP 报文 "窗口字段" 告知的当前未发送但可以发送的最大数据量。
- 接收窗口 :接收方维护的流量控制边界,用于限制 "已接收但未提交给应用层处理" 的数据总量。它的范围由两部分决定:
- 已接收但未处理的数据(暂存于接收缓存)。
- 接收方自身缓存剩余空间对应的当前未接收但可接收的最大数据量。
- 窗口大小的控制逻辑
- 接收方会在每个 TCP 确认报文(ACK)中,将当前接收窗口的剩余可用大小告知发送方;发送方则根据这个值动态调整自己的发送窗口上限,实现双向流量控制。
4.2 TCP拥塞控制
定义:TCP的拥塞控制通过慢启动、拥塞避免、快重传、快恢复来防止网络拥塞。
- 慢启动:
- 初始阶段:当连接刚建立,拥塞窗口从1开始,慢启动阈值通常未64。
- 指数增长:每收到一个ACK,拥塞窗口加倍,直到达到慢启动阈值。
- 拥塞避免:
- 达到阈值:当拥塞窗口达到慢启动阈值,进入拥塞避免状态。
- 线性增长:每收到一个ACK,拥塞窗口增加1,避免网络拥塞。
- 快重传:
检测重复ACK:如果发送方收到三个重复的ACK,立刻重传丢失的数据包。
- 快恢复:
恢复拥塞窗口:重传丢失的包后,拥塞窗口恢复到慢启动的阈值一半,避免拥塞窗口重 置为1。
4.3 超时重传如何实现?超时重传时间如何确定?
- 超时重传:在TCP协议中,如果发送方在预定的时间内没有收到接收方的确认(ACK),则认为数据包丢失,立刻重传丢失包。
- 超时重传的时间(RTO):超时重传的时间是根据来回往返时间(RTT)来决定的,通过动态调整RTO,TCP能够适应网络状况,确保可靠传输。
- RTO的计算:
- SRTT:使用加权平均算法计算平滑RTT的估计值。
- RTT方差:计算RTT的变化幅度。
- 通过SRTT和RTT方差计算超时重传时间(RTO)。
4.4 两台服务器将如何建立多条TCP连接?
- 使用不同端口号或同一端口号的多个套接字实现。每个TCP连接都是由四元组(源IP、目标IP、源端口号、目的端口号)组成,只要改变其中一个就可以建立新的连接。
4.5 滑动窗口过大会怎么样?滑动窗口过小会怎么样?
- 滑动窗口过大:
- 优点:在高带宽、低延迟的网络环境下,较大的滑动窗口可以允许发送方再不等待ACK确认的情况下发送更多数据,充分利用带宽,减少传输时间。
- 缺点:在高延迟或高丢包的网络环境下,较大的滑动窗口可能会导致大量未确认数据堆积,一旦发送丢包或错误,重传的数据量大,浪费带宽,可能导致网络拥塞。
- 滑动窗口过小:
- 优点:在高延迟或高丢包的网络环境下,较小的滑动窗口可以限制未确认数据的数量,减少重传的数据量,降低网络拥塞的风险。
- 缺点:在高带宽、低延迟的网络环境下,较小的滑动窗口可以限制发送方的数据传输速率,频繁等待ACK确认,导致带宽利用率低,传输效率下降。
5.TCP的nagle算法?nagle算法会带来什么坏处?
- nagle算法的目的:
- 减小小包:通过将小数据包合并成大包发送,减少网络中小包的数量。
- 提高效率:减少每个包的协议开销,提高传输效率。
- nagle的工作原理:当发送方有小数据包需要发送时,如果前一个数据包的ACK未收到,新的小数据包会被缓冲,等待前一个包的ACK到达或缓冲区累计足够大的数据量再发送。
- 坏处是增加延迟:数据包不能够立即发送出,需要在等待前一个包ACK到达时,进行数据包累积。
6.KCP是什么
- 基于UDP:KCP是在UDP协议之上实现的,利用UDP的简单和快速传输特点,同时引入了TCP的一些可靠传输机制。
- 目标:通过减少延迟和提高传输速率,为实时应用(在线游戏、实时视频等)提供更好的传输性能。
7. 如何保证UDP的帧与帧间的有序性
- 序列号:
- 发送方:在发送数据包时,为每个数据包添加一个唯一的序列号。序列号从0开始,每发送一个数据包,序列号加1。
- 接收方:在接收数据包时,检查序列号,确保数据包的顺序。如果正确按顺序到达,则将其交给应用层处理。若数据包乱序到达,则交给重排序缓冲区,等待缺失包到达后,按序处理。
- 重排序缓冲区:
接收方:维护一个重排序缓冲区,用于缓存乱序到达的数据包。接收方会检查数据包的 序列号,将其插入到正确的位置。如果发现有缺失,则等待缺失包到达在按序处理。
- ACK确认机制:
接收方:在接收到数据包后,发送一个ACK确认包给发送方,包含已经成功接收的最高 序列号。发送方根据ACK确认包判断哪些包被接收,那些包需要重传。
- 丢包重传机制:
发送方:如果在一定时间内没有接收到接收方的ACK确认包,则认为该数据包已丢失, 发送方立刻重新发送丢失的数据包。
8. 长连接如何维持的
- 定时发送心跳包:设置定时器,定期发送心跳包,保证连接活跃。
- 连接超时检测:设置合理的超时时间,检测连接状态,及时进行重连操作。
- 资源管理:确保系统资源合理分配,避免长时间保持大量连接导致资源耗尽。
9.linux内核收包流程
- 网卡接收数据包:网卡接收到从网络传来的数据包,并将其存储在网卡的接收缓冲区中。
- DMA传输:网卡通过DMA将数据包直接传输到系统内存中,避免CPU参与数据传输,提高效率。
- 中断通知:网卡通过PCle总线生成硬件中断,通知CPU有数据包到达,CPU通过桥接/内存控制器接收中断信号。
- 软中断通知:CPU响应中断,执行硬件中断处理程序,硬件中断处理程序触发软中断,软中断继续处理数据包。
- 内核网络协议栈处理:软中断处理程序将数据包从缓冲区中取出,保存到skb(socket buffer)中。
- 数据包队列:内核网络协议栈进一步处理数据包,将其放入对应socket的接收队列中(data被放到socket的接收队列中)。
- 唤醒用户进程:内核唤醒等待数据的用户进程,用户进程从socket队列中读取数据,进行数据处理。
10.本机向本机发送请求,IP填127.0.0.1和网卡ip地址有区别吗?
- 127.0.0.1(本地回环地址):数据包在内核网络栈中被处理,经过回环接口,不会通过物理网卡发送。用于测试和本地通信。
发送本地回环地址后的流程:
- 回环接口:当应用程序使用127.0.0.1发送请求时,数据包会被路由到回环接口。回环接口是在软件层面模拟的网络接口,专用于本地通信。
- 内核处理:数据包不会离开内核网络栈,而是直接在内核中被处理并返回发送方。数据包不经过物理网卡,不消耗网络带宽。
- 网卡IP地址(本机实际的IP地址):数据包通过内核网络栈被处理,并经过物理网卡发送和接收,走完整的网络栈路径。用于正常的网络通信。
发送网卡ip地址的流程:
- 物理网卡:当应用程序使用本机的实际ip地址发送请求时,数据包会被路由到物理网卡,物理网卡根据MAC地址处理数据包,经过物理网络接口发送和接收。
- 完整的网络栈路径:数据包会走完完整的网络栈路径,包括物理层、数据链路层和网络层。数据包会真正的经过物理网卡,可能在网络上产生负载。
11. TCP粘包如何解决?
- TCP粘包:由于TCP是面向流传输的协议,发送的数据没有边界,接收方可能在一次读取操作中接收到多个发送方的数据包,导致数据粘在一起。
- 解决的三种方法:
- 定长消息:每个消息固定长度,接收方按固定长度读取数据。
- 分隔符:在每个消息之间添加特殊字符作为分隔符,接收方按分隔符拆分数据。
- 消息头标识长度:在每个消息前添加一个消息头,消息头包含该消息的长度,接收方根据长度读取完整消息。


