计算机与网络 - 网络TCP篇(3)
1.3 网络 TCP
1.3.1 TCP 基本认识
1.3.1.1 TCP 头(报文)格式?IP 头(报文)格式?
1. TCP 头重要字段
- 序列号(Sequence Number):这是TCP连接中数据传输的基础,确保数据的有序性和完整性。
- 确认号(Acknowledgment Number):用于确认接收方已经成功接收到的数据,是TCP可靠传输的关键。
- 控制位(Control Flags):特别是SYN、ACK、FIN和RST,它们控制着TCP连接的建立、维护和终止。SYN(同步):用于建立连接。ACK(确认):确认收到的数据。FIN(结束):用于优雅地关闭连接。RST(重置):用于异常情况下的连接重置。
- 窗口大小(Window Size):用于流量控制,告诉发送方接收方还能接收多少数据,是TCP流量控制机制的核心。
- 检验和(Checksum):用于检测TCP头和数据在传输过程中是否出现错误。
2. IP 头重要字段
- 版本(Version):标识IP协议的版本,目前主要使用的是IPv4。
- 总长度(Total Length):包括IP头和数据的总长度,用于确定整个数据包的大小。
- 生存时间(Time to Live, TTL):限制数据包在网络中的传输路径,防止数据包无限循环。
- 协议(Protocol):指示上层协议类型,确保数据包被正确地传递给目标协议处理。
- 源IP地址和目的IP地址:标识数据包的发送方和接收方,是路由选择和数据传输的基础。
- 首部检验和(Header Checksum):用于检测IP头在传输过程中是否出现错误。
1.3.1.2 为什么需要 TCP 协议? TCP 工作在哪一层?
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它需要的原因包括:
- 可靠性:TCP确保数据正确无误地从源传送到目的地。
- 流量控制:TCP通过窗口机制控制发送方的发送速率,防止接收方因接收过多数据而处理不过来。
- 拥塞控制:TCP通过调整窗口大小来控制网络中的流量,防止网络过载。
- 有序传输:TCP保证数据包按照发送顺序到达接收方。
TCP工作在OSI模型的第四层,即传输层。这一层负责在网络中的不同主机之间提供可靠的数据传输服务。
1.3.1.3 什么是 TCP ?
- TCP是一种传输层协议,它提供了端到端的数据传输服务。
- 它通过三次握手建立连接,确保数据的可靠传输,并通过四次挥手来终止连接。
- TCP还负责数据的分段、排序和重组,以及错误检测和重传。
1.3.1.4 什么是 TCP 连接?
- 用于保证可靠性和流量控制维护的某些状态信息的组合,包括 Socket、序列号和窗口大小,称为 TCP 连接。
- 所以建立一个 TCP 连接是需要客户端与服务端达成上述三个信息的共识。Socket:由 IP 地址和端口号组成序列号:用来解决乱序问题窗口大小:用来做流量控制
1.3.1.5 如何唯一确定一个 TCP 连接呢?
一个TCP连接可以通过以下四元组唯一确定:
- 源IP地址:发送方的IP地址。
- 源端口号:发送方的端口号。
- 目的IP地址:接收方的IP地址。
- 目的端口号:接收方的端口号。
1.3.1.6 UDP 和 TCP 有什么区别呢?分别的应用场景是?
TCP和UDP的区别:
- 连接性:TCP是面向连接的,而UDP是无连接的。
- 可靠性:TCP提供可靠的数据传输,而UDP不保证数据包的到达。
- 速度:UDP通常比TCP快,因为它没有建立连接和维护连接的开销。
- 数据顺序:TCP保证数据包的顺序,而UDP不保证。
- 错误检测和重传:TCP有错误检测和重传机制,UDP没有。
应用场景:
- TCP:适用于需要可靠传输的应用,如网页浏览(HTTP)、文件传输(FTP)、邮件传输(SMTP)等。
- UDP:适用于对实时性要求高的应用,如视频会议、在线游戏、DNS查询等。
1.3.1.7 TCP 和 UDP 可以使用同一个端口吗?
- TCP和UDP可以使用同一个端口,端口号是用来区分同一主机上运行的不同服务的。
- 一个端口可以被TCP服务监听,也可以被UDP服务监听,但不能同时被两者监听。
- 如果一个端口已经被TCP服务占用,那么UDP服务就不能使用这个端口,反之亦然。
1.3.1.8 TCP 和 IP 的区别?TCP 和 UDP 的区别?
1. TCP 和 IP 的区别:
- TCP(Transmission Control Protocol,传输控制协议),位于传输层,负责在两个主机之间的数据传输。TCP 确保数据包正确无误地从源传送到目的地,如果在传输过程中丢失或损坏,TCP 会重发数据直到接收方正确接收。
- IP(Internet Protocol,互联网协议),位于网络层,负责将数据包从源传送到目的地。IP 不保证数据包的顺序或可靠性,只是简单地将数据包从一个地方传到另一个地方。使用 IP 地址标识网络上的设备。
2. TCP 和 UDP 的区别:
- TCP(Transmission Control Protocol,传输控制协议)面向连接:在数据传输前,需要建立一个连接。可靠且有序:提供数据包确认、超时重传、数据顺序保证等机制,确保数据可靠传输。流量控制:根据接收方的接收能力调整发送速率。拥塞控制:网络拥塞时,减少数据的发送量。
- UDP(User Datagram Protocol,用户数据报协议)无连接:不需要建立连接,可以直接发送数据。不可靠且不保证有序:不提供数据包确认或重传机制,数据可能会丢失或乱序到达。简单快速:由于没有复杂的控制机制,UDP的开销小,适用于对实时性要求高的应用。无流量控制和拥塞控制:发送方可以自由地发送数据,不考虑网络状况和接收方的处理能力。
1.3.2 TCP 连接建立
1.3.2.1 TCP 三次握手?TCP 四次挥手?状态触发条件?
1. TCP 三次握手
- 第一次握手:客户端随机生成初始序列号 Client_ISN,设置 SYN(同步序列编号)标志位为 1,表示这是一个连接请求。客户端发送一个带有 SYN 标志位的报文段给服务端,之后客户端进入 SYN-SENT 状态。
- 第二次握手:服务端接收到客户端的报文后,也随机生成初始序列号 Server_ISN,设置 SYN 和 ACK 标志位为 1。服务端将确认号(ACK number)字段设置为 Client_ISN + 1,以确认客户端的 SYN 报文。服务端发送带有 SYN 和 ACK 标志位的报文段给客户端,之后服务端进入 SYN-RCVD 状态。
- 第三次握手:客户端接收到服务端的报文后,设置 ACK 标志位为 1,将确认号字段设置为 Server_ISN + 1,以确认服务端的报文。客户端发送带有 ACK 标志位的报文段给服务端,之后客户端和服务端都进入 ESTABLISHED 状态。
2. TCP 四次挥手
- 第一次挥手:客户端发送 FIN 报文告诉服务端它已经完成数据发送,进入 FIN_WAIT_1 状态。
- 第二次挥手:服务端收到后回复 ACK 报文,并进入 CLOSE_WAIT 状态。
- 第三次挥手:服务端发送 FIN 报文告诉客户端它也完成数据发送,进入 LAST_ACK 状态。
- 第四次挥手:客户端收到 FIN 报文后回复 ACK 报文,并进入 TIME_WAIT 状态等待 2MSL,以确保服务端收到最后的 ACK。
3. 如果两个服务器同时发出请求,TCP 需要几次握手?
如果两个服务器同时发出请求,它们会各自启动一个三次握手的过程,同时扮演客户端和服务器的角色。
这样,两个服务器之间就建立了两个独立的TCP连接,每个连接都需要三次握手,总共是六次握手过程。
4. TCP 可以简化为三次挥手吗?
- 数据传输不完整:如果服务器在发送完数据后立即关闭连接,而没有等待客户端的FIN确认,那么客户端可能无法确保服务器已经接收到所有的数据。
- 状态同步问题:四次挥手确保了客户端和服务器都能确认对方已经完成了数据传输。如果简化为三次挥手,可能会造成状态不同步,即一方认为连接已经关闭,而另一方还在等待数据传输的确认。
1.3.2.2 如何在 Linux 系统中查看 TCP 状态?
Linux 查看系统中所有TCP连接以及它们关联的进程信息,可以在终端中输入命令:
netstat -napt
。
1.3.2.3 为什么是三次握手?不是两次、四次?
三次握手是为了同时满足两个条件:
- 确保双方的接收和发送通道都是开放的:三次握手可以确保客户端和服务器都能发送和接收数据。
- 防止已失效的连接请求突然传送到服务器,因而产生错误:如果使用两次握手,服务器可能无法区分新旧连接请求。
四次握手则没有必要,因为第三次握手同时完成了数据的发送和确认,使得连接可以建立。
1.3.2.4 为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?
初始化序列号(ISN)的随机性是为了确保安全性和防止某些类型的攻击:
- 防止会话劫持:如果ISN是固定的,攻击者可以预测序列号,从而劫持会话。
- 防止重复数据包的处理:随机的ISN使得旧的或重放的数据包在新的连接中被识别并丢弃。
1.3.2.5 初始序列号 ISN 是如何随机产生的?
- ISN的生成通常是由操作系统在建立连接时随机选择的。
- 这个随机数是通过一个加密的随机数生成器产生的,以确保每次连接的ISN都是唯一的。
1.3.2.6 既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?
MSS(最大段大小)是TCP层的一个参数,它指定了TCP数据段的最大长度,不包括TCP头部。MSS的存在是为了:
- 避免 IP 层分片:通过限制TCP数据段的大小,可以减少IP层的分片,因为分片会降低网络性能。
- 提高效率:MSS 允许发送方一次性发送更大的数据块,减少了头部开销,提高了传输效率。
1.3.2.7 第一次握手丢失了,会发生什么?
- 如果第一次握手(客户端到服务器的SYN)丢失了,客户端会因为超时而重新发送SYN包。
- 如果连续多次重试后仍然失败,客户端会放弃并报告错误。
1.3.2.8 第二次握手丢失了,会发生什么?
- 如果第二次握手(服务器到客户端的SYN-ACK)丢失了,服务器会因为超时而重新发送SYN-ACK。
- 如果连续多次重试后仍然失败,服务器会放弃并报告错误。
1.3.2.9 第三次握手丢失了,会发生什么?
- 如果第三次握手(客户端到服务器的ACK)丢失了,服务器会因为超时而重新发送SYN-ACK。
- 客户端在收到重复的SYN-ACK后,会重新发送ACK来完成握手。
1.3.2.10 什么是 SYN 攻击?如何避免 SYN 攻击?
SYN攻击是一种拒绝服务攻击(DoS),通过发送大量的SYN请求来消耗服务器的资源,导致服务器无法处理合法的连接请求。
避免SYN攻击的方法:
- SYN Cookies:这是一种技术,服务器在收到SYN请求时,不立即分配资源,而是发送一个SYN-ACK,并要求客户端使用特定的序列号(基于服务器的预期)来响应。
- Rate Limiting:限制SYN请求的速率,超过一定阈值的IP地址会被暂时禁止。
- 增加资源:增加服务器的处理能力和连接队列,以应对大量的SYN请求。
- 使用防火墙和入侵检测系统:监控和过滤异常流量,防止SYN攻击。
1.3.2.11 TCP 重传机制与拥塞控制?
1. TCP 重传机制
想象一下,你在网上买东西,卖家把商品寄给你。但是,有时候包裹可能会在半路上丢了,或者被弄坏了。
- 超时重传:卖家寄出包裹后,会等一段时间,如果你没确认收到,他就会觉得包裹可能丢了,然后就会再寄一个给你。
- 快速重传:如果你收到了好几个包裹,但是中间的一个没收到,你会告诉卖家。卖家马上再寄一个给你,不用等太久。
- 选择性确认(SACK):你告诉卖家哪些包裹收到了,哪些没收到,这样只需要寄没收到的,不用把已经收到的再寄一遍。
- D-SACK:你告诉卖家收到了一个包裹两次,这样卖家就知道可能是路上出了问题,或者他自己不小心多发了一次。
2. TCP 拥塞控制
想象一下,卖家要同时给很多人寄包裹,如果一下子寄太多,路上可能会堵车(网络拥塞),这样大家都收不到包裹了。
- 慢启动:卖家一开始不敢寄太多包裹,先寄几个试试,看看路上会不会堵车。
- 拥塞避免:如果路上看起来还行,卖家就会慢慢增加包裹的数量,但是不会像一开始那样一下子增加很多。
- 拥塞发生:如果卖家发现路上开始堵车了(比如包裹丢了,或者你很久没收到包裹),他就会减少包裹的数量。
- 拥塞窗口回退:如果卖家发现路上太堵了,他就会减少包裹的数量,等路不堵了再慢慢增加。
- 快速重传和快速恢复:如果卖家发现你少收了一个包裹,会马上再寄一个给你,然后等路不堵了,再慢慢增加包裹的数量。Tahoe和Reno算法:Tahoe 比较老,一堵车就全部重来。Reno 改进了一些,如果只丢了几个包裹,就不用全部重来。NewReno算法:这个算法是Reno的升级版,如果路上只是有点小堵,卖家可以更快地恢复正常的包裹数量。BBR算法:这是一种新算法,卖家会先看看路有多宽(带宽),然后根据宽度来决定寄多少包裹,这样就不会堵车了。
1.3.3 TCP 连接断开
1.3.3.1 TCP 四次挥手过程是怎样的?
TCP四次挥手是终止一个TCP连接的过程,具体步骤如下:
- 客户端发送FIN:客户端决定关闭连接,发送一个FIN标志位的段给服务器,表示客户端不再发送数据了。
- 服务器接收FIN并发送ACK:服务器接收到FIN后,发送一个ACK确认给客户端,确认收到了FIN。
- 服务器发送FIN:服务器完成所有数据发送后,发送一个FIN给客户端,表示服务器也不再发送数据了。
- 客户端接收FIN并发送ACK:客户端接收到FIN后,发送一个ACK确认给服务器,确认收到了FIN。
1.3.3.2 为什么挥手需要四次?
- 挥手需要四次的原因是因为TCP是一个全双工的协议,每个方向都可以独立地关闭。
- 因此,每个方向都需要一个FIN和一个ACK来关闭。
1.3.3.3 第一次挥手丢失了,会发生什么?
- 如果客户端发送的FIN丢失了,客户端会因为超时而重新发送FIN。
- 服务器如果没有收到FIN,会继续等待,直到客户端重传FIN或者客户端因为超时放弃关闭连接。
1.3.3.4 第二次挥手丢失了,会发生什么?
- 如果服务器发送的ACK丢失了,客户端会因为超时而重新发送FIN。
- 服务器会重新发送ACK,直到客户端收到并确认。
1.3.3.5 第三次挥手丢失了,会发生什么?
- 如果服务器发送的FIN丢失了,客户端不会关闭连接,会继续等待FIN。
- 服务器会因为超时而重新发送FIN,直到客户端收到并确认。
1.3.3.6 第四次挥手丢失了,会发生什么?
- 如果客户端发送的ACK丢失了,服务器会因为超时而重新发送FIN。
- 客户端会重新发送ACK,直到服务器收到并确认。
1.3.3.7 为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是任何TCP段在网络中的最大生存时间。TIME_WAIT状态等待2MSL的原因是:
- 确保数据传输完成:确保网络上所有重复的FIN和ACK都被丢弃,避免新连接被误认为是旧连接的重复。
- 允许足够的时间:确保足够的时间让TCP段在网络中消失,避免新旧连接混淆。
1.3.3.8 为什么需要 TIME_WAIT 状态?
TIME_WAIT状态的需要是因为:
- 防止旧数据干扰:确保所有重复的数据包都被丢弃,防止它们干扰新的连接。
- 允许足够的时间:允许足够的时间让TCP段在网络中消失。
1.3.3.9 TIME_WAIT 过多有什么危害?
TIME_WAIT过多可能导致:
- 资源占用:占用大量的文件描述符和内存资源。
- 端口耗尽:如果端口数量有限,可能导致端口耗尽,影响新连接的建立。
1.3.3.10 如何优化 TIME_WAIT?
优化TIME_WAIT的方法包括:
- 调整MSL值:在某些系统中,可以调整MSL值来减少TIME_WAIT时间。
- 使用SO_REUSEADDR套接字选项:允许应用程序重用处于TIME_WAIT状态的端口。
1.3.3.11 服务器出现大量 TIME_WAIT 状态的原因有哪些?
服务器出现大量TIME_WAIT状态的原因可能包括:
- 客户端频繁连接和断开:客户端频繁地建立和关闭连接。
- 服务器重启:服务器重启后,之前的连接会进入TIME_WAIT状态。
1.3.3.12 服务器出现大量 CLOSE_WAIT 状态的原因有哪些?
服务器出现大量CLOSE_WAIT状态的原因可能包括:
- 客户端未关闭连接:客户端发送了FIN,但服务器端应用程序没有关闭连接。
- 服务器端应用程序问题:服务器端应用程序没有正确处理连接关闭。
1.3.3.13 如果已经建立了连接,但是客户端突然出现故障了怎么办?
- 如果客户端出现故障,服务器会因为超时而检测到连接丢失,并关闭连接。
1.3.3.14 如果已经建立了连接,但是服务端的进程崩溃会发生什么?
- 如果服务端的进程崩溃,操作系统会发送一个RST(重置)标志位的段给客户端,通知客户端连接已被关闭。
- 客户端会收到一个错误并关闭连接。
1.3.4 Socket 编程
1.3.4.1 针对 TCP 应该如何 Socket 编程?
TCP Socket编程通常遵循以下步骤:
- 创建Socket:使用socket()函数创建一个新的socket。
- 绑定Socket:使用bind()函数将socket与一个地址和端口绑定。
- 监听连接:对于服务器端,使用listen()函数监听传入的连接。
- 接受连接:服务器端使用accept()函数接受一个传入的连接。
- 数据传输:使用send()和recv()函数进行数据的发送和接收。
- 关闭连接:使用close()函数关闭socket。
1.3.4.2 listen 时候参数 backlog 的意义?
listen()
函数中的backlog
参数指定了内核应该为相应socket排队的最大连接个数。参数的意义在于:
- 限制队列长度:防止服务器因为过多未处理的连接请求而耗尽资源。
- 提高效率:允许服务器有序处理传入的连接请求。
1.3.4.3 accept 发生在三次握手的哪一步?
accept()
调用发生在TCP三次握手的第三步。当服务器调用accept()
时,它会:
- 检查半连接队列:查看是否有已完成三次握手的连接请求。
- 移除连接请求:从半连接队列中移除请求,并为该连接创建一个新的socket。
- 返回新的socket:返回一个新的socket,用于与客户端通信。
1.3.4.4 客户端调用 close 后,连接断开的流程是什么?
客户端调用
close()
后,TCP连接断开的流程如下:
- 发送FIN:客户端发送一个FIN段,表示关闭连接的意图。
- 等待ACK:客户端等待服务器的ACK确认。
- 接收数据:服务器可以继续发送数据,直到它也决定关闭连接。
- 服务器发送FIN:服务器发送FIN段给客户端。
- 客户端发送ACK:客户端发送ACK确认给服务器。
- TIME_WAIT状态:客户端进入TIME_WAIT状态,等待足够的时间以确保服务器收到最后的ACK。
- 连接关闭:经过2MSL后,客户端关闭连接。
1.3.4.5 没有 accept,能建立 TCP 连接吗?
- 不能。accept()函数是必需的,因为它用于接受传入的连接请求,并为已建立的连接创建一个新的socket。
- 没有accept(),服务器无法获取到客户端的连接请求,也就无法建立TCP连接。
1.3.4.6 没有 listen,能建立 TCP 连接吗?
#八股文##计算机网络#
- 不能。listen()函数是必需的,因为它告诉操作系统该socket准备接受传入的连接请求。
- 没有listen(),socket不会监听任何传入的连接,因此无法建立TCP连接。
- 服务器端必须先调用listen(),然后才能调用accept()来接受连接。