编码不易,转载请注意出处!
本文按照下面图片中的问题依次深入了解tcp相关知识
本次文章不涉及 TCP 流量控制、拥塞控制、可靠性传输等方面知识,这些留在下篇!
TCP基本认识
瞧瞧tcp头格式
我们先来看看 TCP 头的格式,标注颜色的表示与本文关联比较大的字段,其他字段暂不做详细阐述。
序列号: 在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
确认应答号: 指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决不丢包的问题。
控制位:
- ACK : 该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1
- RST : 该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
- SYN : 该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
- FIN : 该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
为什么需要 TCP 协议? TCP 工作在哪一层?
因为ip层也就是网络层不提供可靠的数据交互,它的职责主要是把数据发送到对方。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责
什么是 TCP ?
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
- 面向连接: 一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
- 可靠的: 无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文总是能够到达接收端;
- 字节流: 消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。
什么是TCP连接
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
所以我们可以知道,建立一个 TCP 连接是需要客户端与服务器端达成上述三个信息的共识。
- Socket: 由 IP 地址和端口号组成
- 序列号: 用来解决乱序问题等
- 窗口大小: 用来做流量控制
如何唯一确定一个 TCP 连接呢?
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
- 源地址
- 源端口
- 目标地址
- 目标端口
源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
一个tcp连接是由(源地址、源端口、目标地址、目标端口),目标地址和目标端口其实就是服务器的ip和port,是不会变的,所有只需要计算客户端的地址数和端口数有多少种组合就行了。
对 IPv4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最大 TCP 连接数,约为 2 的 48 次方。
当然,这只是理论上限,而服务端最大并发 TCP 连接数远不能达到理论上限。
- 一个tcp连接就是一个socket,首先主要是文件描述符限制, Socket 都是文件,所以首先要通过 ulimit 配置文件描述符的数目;
- 另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的。
UDP是什么?
UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。
UDP 协议非常简单,头部只有 8 个字节( 64 位),UDP 的头部格式如下:
- 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
- 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
- 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。
UDP 和 TCP 有什么区别呢?
连接
TCP 是面向连接的传输层协议,传输数据前先要建立连接。
UDP 是不需要连接,即刻传输数据。服务对象
TCP是一对一的两点服务。
UDP支持一对一、一对多、多对多的交互通信可靠性
TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
UDP 是尽最大努力交付,不保证可靠交付数据。拥塞控制、流量控制
TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。首部开销
TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
UDP 首部只有 8 个字节,并且是固定不变的,开销较小。传输方式
TCP 是流式传输,没有边界,但保证顺序和可靠。
UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。分片不同
TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
UDP 的数据大小如果大于 MTU(TCP提交给IP层最大分段) 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。
TCP和UDP的应用场景是?
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:ftp文件传输、http/https
由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:视频、音频等多媒体通信、广播通信、包总量较少的通信,如 DNS 、SNMP 等
为什么 UDP 头部没有「首部长度」字段,而 TCP 头部有「首部长度」字段呢?
原因是 TCP 有可变长的「选项」字段,而 UDP 头部长度则是不会变化的,无需多一个字段去记录 UDP 的首部长度。
三次握手每一次都可以携带数据吗?
第三次握手是可以携带数据的,前两次握手是不可以携带数据的
TCP连接建立
如何在 Linux 系统中查看 TCP 状态?
TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。
为什么是三次握手?不是两次、四次?
因为三次握手已经能够让双方达到信息对称的一个效果了,就是说已经让对方知道我具有接收和发送的能力了。所以不需要使用更多的通信次数。
为什么客户端和服务端的初始序列号 ISN 是不相同的?
消除历史连接的影响。如果一个已经失效的连接被重用了,但是该旧连接的历史报文还残留在网络中,如果序列号相同,那么就无法分辨出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。所以,每次建立连接前重新初始化一个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。
方面是为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收。
初始序列号 ISN 是如何随机产生的?
起始 ISN 是基于时钟的,每 4 毫秒 + 1,转一圈要 4.55 个小时。
TCP连接断开
TCP四次挥手
为什么挥手需要四次?
再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。
- 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
- 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。
从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。
为什么 TIME_WAIT 等待的时间是 2MSL?
MSL 是 Maximum Segment Lifetime,报文最大生存时间.
2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
为什么需要 TIME_WAIT 状态?
主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
需要 TIME-WAIT 状态,主要是两个原因:
- 防止具有相同「四元组」的「旧」数据包被收到;
- 保证「被动关闭连接」的一方能被正确的关闭,即保证最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭;
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP 有一个机制是保活机制。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
accept 发生在三次握手的哪一步?
服务端 accept 成功返回是在三次握手成功之后。