位码即TCP标志位

  • S=SYN 发起连接标志

  • P=PUSH 传送数据标志

  • F=FIN 关闭连接标志

  • R=RESET 异常关闭连接,链接重置

  • . 表示没有任何标志,表示返回ack

201703281490711840630122.png

第一次握手:

客户端发送一个TCP的SYN标志位置1的包,ACK=0,TCP规定SYN=1时不能携带数据,当SYN=1而ACK=0时,表明这是一个连接请求报文但要消耗一个序号,因此声明自己的序号是seq=i。Seq:序号,4字节,范围为0^32—1^32,共4284967296,达到时重新开始计算。Clinet进入SYN_SENT状态,等待Server确认。所以最终客户端发送的报文中包含SYN=1,ACK=0,seq=i(i为一个随机数)。

第二次握手:

服务器发回确认包(ACK)应答。即SYN=1 ACK=1,因为建立连接,则应在响应报文中使SYN=1和ACK=1。seq=j(产生的随机包序号),ack=i+1(确认客户端序号有效)。所以发送给客户端的包里面包含:SYN=1,ACK=1,seq=j,ack=i+1,此时服务器进入SYN_RECV状态。

第三次握手:

客户端收到返回的包进行确认,检查ack是否为i+1(也就是是不是自己第一次发起请求时候产生的随机序号+1),ACK是否为1,如果正确则将标志位ACK置为1,并将ack=j+1(即在服务器序号的基础上加1),seq=i+1(也就是最早的seq序号)发送给服务器端,服务器收到后确认seq=i+i,ACK=1,ack=j+1,服务端验证没有问题随建立连接,并开始打开端口为客户端发送数据。客户端服务端进入ESTABLISHED状态,完成三次握手。

201703291490797310442123.png

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

不管是客户端还是服务器端都可以调用close之类的函数主动终止一个连接。这里以客户端主动断开连接举例。

第一次握手

  客户端调用close函数,主动发送一个FIN报文给服务器端,用来关闭客户端到服务器的数据传送,此时客户端进入TIME_WAIT1状态。

  FIN报文也可能附加用户数据。发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送)但是还可以接收数据。

  当调用recv时,如果返回0就表示对端关闭。这个时候通常被关闭端也调用close,然后TCP层发送FIN,继续完成四次握手。如果被关闭端不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。

  FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

第二次握手:

   服务器收到这个FIN,它发回一个ACK,确认号为收到的序号加1,和SYN一样,一个FIN将占用一个序号,此时服务器进入CLOSE_WAIT状态,客户端进入TIME_WAIT2状态。

第三次握手:

   当服务器端也没有要传送的数据时,服务器关闭与客户端的连接,发送一个FIN给客户端A,服务器进入LAST_ACK状态。

第四次握手:

   客户端发回ACK报文确认,并将确认号设置为收到序号加1,服务器收到报文并确认成功,服务器端进入CLOSED状态,客户端进入TIME_WAIT(表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。),等待2MSL--120s-240s(1、确保连接可靠地关闭; 即防止最后一个ACK丢失。2、避免产生套接字混淆(同一个端口对应多个套接字),没有收到任何回复,则证明server端已正常关闭,客户端也就关闭了。

参考:http://www.51niux.com/?id=100