1 客户端的协议栈向服务器端发送了SYN包,并告诉服务器端当前发送序列号j,客户端进入SYNC_SENT状态;服务器
2 服务器端的协议栈收到这个包以后,和客户端进行ACK应答,应答的值为j+1,表示对SYN包j的确认,同时服务器也发送一个SYN包,告诉客户端当前个人发送序列号并发
为k,服务器端进入SYNC_RCVD状态;编码
3 客户端协议栈收到ACK以后,使得应用程序从connect调用返回,表示客户端到服务器端的单向链接创建成功,客户端的状态为ESTABLISHED,同时客户端协议栈也设计
会对服务器端的SYN包进行应答,应答数据为k+1;blog
应答包到达服务器端后,服务器端协议栈使得accept阻塞调用返回,这个时候服务器端到客户端的单向链接也创建成功,服务器端也进入ESTABLISHED状态。ip
形象一点的比喻是这样的,有A和B想进行通话:内存
A先对B说:“喂,你在么?我在的,个人口令是j。”资源
B收到以后大声回答:“我收到你的口令j并准备好了,你准备好了吗?个人口令是k。”it
A收到以后也大声回答:“我收到你的口令k并准备好了,咱们开始吧。”基础
能够看到,这样的应答过程总共进行了三次,这就是TCP链接创建之因此被叫为“三次握手”的缘由了。
咱们能够给 UDP 找一个相似的例子,这个例子就是邮寄明信片。在这个例子中,发信方在明信片中填上了接收方的地址和邮编,投递到邮局的邮筒以后,就能够无论
了。发信方也能够给这个接收方再邮寄第二张、第三张,甚至是第四张明信片,可是这几张明信片之间是没有任何关系的,他们的到达顺序也是不保证的,有可能最后寄出的
第四张明信片最早到达接收者的手中,由于没有序号,接收者也不知道这是第四张寄出的明信片;并且,即便接收方没有收到明信片,也没有办法从新邮寄一遍该明信片。
TCP 是一个面向链接的协议,TCP 在 IP 报文的基础上,增长了诸如重传、确认、有序传输、拥塞控制等能力,通讯的双方是在一个肯定的上下文中工做的。
而 UDP 则不一样,UDP 没有这样一个肯定的上下文,它是一个不可靠的通讯协议,没有重传和确认,没有有序控制,也没有拥塞控制。咱们能够简单地理解为,在 IP 报
文的基础上,UDP 增长的能力有限。
UDP 不保证报文的有效传递,不保证报文的有序,也就是说使用 UDP 的时候,咱们须要作好丢包、重传、报文组装等工做。
首先,一方应用程序调用 close,咱们称该方为主动关闭方,该端的 TCP 发送一个 FIN 包,表示须要关闭链接。以后主动关闭方进入 FIN_WAIT_1 状态。
接着,接收到这个 FIN 包的对端执行被动关闭。这个 FIN 由 TCP 协议栈处理,咱们知道,TCP 协议栈为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,应用程序可
以经过 read 调用来感知这个 FIN 包。必定要注意,这个 EOF 会被放在已排队等候的其余已接收的数据以后,这就意味着接收端应用程序须要处理这种异常状况,由于 EOF
表示在该链接上再无额外数据到达。此时,被动关闭方进入 CLOSE_WAIT 状态。
接下来,被动关闭方将读到这个 EOF,因而,应用程序也调用 close 关闭它的套接字,这致使它的 TCP 也发送一个 FIN 包。这样,被动关闭方将进入 LAST_ACK 状态。
最终,主动关闭方接收到对方的 FIN 包,并确认这个 FIN 包。主动关闭方进入 TIME_WAIT 状态,而接收到 ACK 的被动关闭方则进入 CLOSED 状态。进过 2MSL 时间
以后,主动关闭方也进入 CLOSED 状态。
你能够看到,每一个方向都须要一个 FIN 和一个 ACK,所以一般被称为四次挥手。
固然,这中间使用 shutdown,执行一端到另外一端的半关闭也是能够的。
TCP 链接终止时,主机 1 先发送 FIN 报文,主机 2 进入 CLOSE_WAIT 状态,并发送一个 ACK 应答,同时,主机 2 经过 read 调用得到 EOF,并将此结果通知应用程
序进行主动关闭操做,发送 FIN 报文。主机 1 在接收到 FIN 报文后发送 ACK 应答,此时主机 1 进入 TIME_WAIT 状态。
主机 1 在 TIME_WAIT 停留持续时间是固定的,是最长分节生命期 MSL(maximum segment lifetime)的两倍,通常称之为 2MSL。和大多数 BSD 派生的系统同样,
Linux 系统里有一个硬编码的字段,名称为TCP_TIMEWAIT_LEN,其值为 60 秒。也就是说,Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
过了这个时间以后,主机 1 就进入 CLOSED 状态。只有发起链接终止的一方会进入 TIME_WAIT 状态。
为何不直接进入 CLOSED 状态,而要停留在 TIME_WAIT 这个状态
首先,这样作是为了确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
TCP 在设计的时候,作了充分的容错性设计,好比,TCP 假设报文会出错,须要重传。在这里,若是图中主机 1 的 ACK 报文没有传输成功,那么主机 2 就会从新发送 FIN 报文。
若是主机 1 没有维护 TIME_WAIT 状态,而直接进入 CLOSED 状态,它就失去了当前状态的上下文,只能回复一个 RST 操做,从而致使被动关闭方出现错误。
如今主机 1 知道本身处于 TIME_WAIT 的状态,就能够在接收到 FIN 报文以后,从新发出一个 ACK 报文,使得主机 2 能够进入正常的 CLOSED 状态。
过多的 TIME_WAIT 的主要危害有两种。
第一是内存资源占用,这个目前看来不是太严重,基本能够忽略。
第二是对端口资源的占用,一个 TCP 链接至少消耗一个本地端口。要知道,端口资源也是有限的,通常能够开启的端口为 32768~61000 ,也能够经过
net.ipv4.ip_local_port_range指定,若是 TIME_WAIT 状态过多,会致使没法建立新链接。这个也是咱们在一开始讲到的那个例子。