TCP三次握手和四次挥手详解

传输层:

传输层: 负责端与端之间的数据传输;TCP/UDP协议

UDP协议(用户数据报协议):
TCP三次握手和四次挥手详解
16位源端口/目的端口: 负责端与端之间的是数据传输,从哪里来往哪里去;
16位UDP数据报长度: 包含在头部内面,标记UDP数据报的长度;
16位UDP数据校验和: 检验数据是否正确—二进制反码求和算法
注意:
16位的数据报长度:表示的是整个UDP数据报的最大长度
因为UDP的性质所致,所以当校验和出错时,数据报就会被直接丢掉;
UDP协议的特点:

  • 无连接、不可靠
    客户端只需要知道服务端的地址信息就可以直接发送数据,并且不关心数据是否已经安全到达;
  • 面向数据报
    1、特性取决于UDP数据报长度这个字段–uint16_t决定了UDP整个数据报长度不能大于64k(2^16);
    2、因为UDP数据包长度限制,因此若数据过大,则需要用户在应用层进行数据分包,将大数据分割成一个个的小数据包64k的大小;
    3、UDP并不保证数据的可靠,有序到达,因此也有可能乱序,需要用户在应用层进行包序管理
    4、UDP在sendto发送数据时将数据直接交给内核由内核由内核将数据传给网络层协议,则会直接封装头部,将数据发送出去(每一次的sendto发送的数据都会封装一个UDP头部);

在传输层使用UDP协议的应用层协议:
DHCP:动态主句配置协议
DNS:域名解析协议

TCP协议(传输控制协议):
TCP三次握手和四次挥手详解
16位源端口/目的端口: 表示数据从哪个进程来,到哪个进程去;
32位序号/确认序号: 进行包序管理,一般序号字段有32位,可对4G的数据进行编号。当序号重复使用时可以保证旧序号的数据早已到达终点
长度: 其实就是表示TCP报文段首部长度; 以4字节为单位;表示TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度15 * 4 = 60字节
6位保留: 为今后使用,但是目前设置为0;
标志位:

  • URG:紧急指针是否有效
  • ACK:确认号是否有效
  • PSH:提醒接收端应用程序立刻从TCP缓冲区把数据读走
  • RST:挺行对方重新建立连接;也叫复位报文段
  • SYN:请求建立连接;也叫同步报文段
  • FIN:通知对方本段要关闭;也叫结束报文段

16位窗口大小: 用于避免丢包的一种方式
16位校验和: 检查数据一致性—二进制反码求和
16位紧急指针: 表示那部分数据谁紧急数据,在URG=1时才有意义;
40字节头部选项: 通信双方协商一些信息的时候使用
数据: 当没有使用选项时,该项才有意义,填充就是为了使TCP首部长度是4字节的整倍数

TCP连接管理

TCP协议特点:我们说TCP协议是一个有状态的协议从连接还断开就可以明显感觉到
建立连接:三次握手
TCP三次握手和四次挥手详解

  1. 开始创建了两个套接字,两端都处于CLOSED状态,都进行地址的绑定,然后显示服务端进入监听状态
  2. 客户端向服务端发送链接请求,发送一个请求报文:SYN=1,seq=x;注意:SYN数据报不携带数据,但是要消耗一个初始序号;在发送过后客户端进入SYN-SENT(同步已发送状态)状态
  3. 服务端收到后,如果同意A的请求,就要回复发送确认;发送一个确认报文:SYN=1,ACK=1,ack=x+1(确认序号),seq=y(初始序号); 表示你的请求我同意了,同时也想你发送请求;注意: 这个报文段不能携带数据,但是要消耗一个序号;在发送过后服务端进入 SYN-RECV(同步收到)状态
  4. 客户端收到服务端的确认后,还要给服务端进行恢复;此时客户端会给服务端发送一个A确认报文:ACK=1,seq=x+1,ack=y+1;注意: 规定ACK报文可以携带数据,但如果不携带数据就不消耗序号; 所以可以发现这个报文的序号还是上一个报文的序号的原因就是如此;在发送过后客户端进入ESTABLISHED(已建立连接状态)状态
  5. 当服务端收到客户端的确认后;服务端也就进入ESTABLISHED(已建立连接状态)状态
  6. 当服务端和客户端都进入ESTABLISHED(已建立连接状态)状态时,说明三次握手建立连接的过程到此也就结束了,服务端与客户端此时就可以进行数据的传输了;

问题:

  1. 在三次握手中,服务端给客户端进行回复时,回复的那个报文也可以拆分成两个报文,先发送一个确认报文(ACK=1,ack=x+1),然后在发送一个同步报文(SYN=1,seq=y),这样就变成了四次握手,但是但是最后的效果都是一样的,为什么不进行四次握手?

解答:从功能上来看三次握手和四次握手达到的效果是一样,但是四次的话就意味着服务端就要多发一次这也就意味消耗,并且TCP通信时全双工通信不但可以接受还可以发送,这样避免不必要的资源浪费;

  1. 为什么在客户端在接收到服务端的回复后(问什么不进行两次握手),还要在发送因此确认呢?

解答:防止已经失效的连接请求报文段突然后传送到服务端,
1、此时设想一个情节,客户端向服务端发送一个报文请求,但是这个报文直接就丢失的(这种情况在网络中很常见),这是客户端就会重传一次连接请求,这次重传的连接请求和服务端建立了连接进行了数据传输;传输完成后就断开连接;此时客户端发送了两个请求报文,但是第一个报文丢失了,第二个报文与服务端进行了连接;
2、但是此时出现了一种异常情况,就是客户端第一次发送的请求报文并没有丢失而是在某一个网络节点长时间滞留了,以至于延误到新连接已经和服务端完成后释放以后的某个时间这个第一个延迟的请求报文才达到服务端,本来这是一个早都失效的报文,但是服务端收到后误认为客户端又要再一次和它进行数据传输,此时就创建一个新的套接字来和客户端进行传输,服务端向客户端回复一个确认报文,,假设没有第三次握手,那么此时的服务端就已经和客户端再次进行连接了,但事实是客户端此时并没有发出连接请求,那么此时客户端就不会理睬服务端的确认报文,也不会向服务端发送数据,但是此时服务端确认为的连接已经建立了一直等待客户端的数据。就这样服务端的资源就这样被消耗;
3、但是有了三次握手那就不一样了,当服务端向客户端发送确认报文时,但是客户端不理会,服务端迟迟收不到第三次来自客户端的确认报文(第三次为握手),服务端就会知道客户端并没有像和其建立连接,他就会释放此时的资源,重新等待新的连接;

总结一下就是: 两次不安全,四次没必要


关闭连接:四次挥手
TCP三次握手和四次挥手详解

  1. 当数据传输完成后,服务端和客户端都有可能释放连接,此时客户端和服务端都处于ESTABLISHED(已建立连接状态)状态
  2. 客户端首先向其TCP发出释放连接报文段(close()接口/shutdown()接口),并且停止数据发送主动关闭TCP连接;这是客户端向服务端发送一个连接释放报文:FIN=1,seq=u(序号其实等于客户端前面已经传输数据的最后一条的最后一个字节的序号+1);注意:规定,FIN报文即使不携带数据,它也消耗掉一个序号;在发送报文后客户端处于FIN-WAIT-1(终止等待1)状态
  3. 服务端收到连接释放报文后,会回复客户端一个确认报文:ACK=1, seq=v(序号其实等于服务端前面已经传输数据的最后一条的最后一个字节的序号+1),ack=u+1;注意: 规定ACK报文可以携带数据,但如果不携带数据就不消耗序号; 在服务端发送确认报文后服务端处于:CLOSE-WAIT(关闭等待)状态;此时其实整个TCP连接处于半关闭(half-close)状态;意思就是客户端没有数据给服务端了,但是要是服务端如果在发送数据客户端还要接受;(服务端 -----> 客户端这个方向的链接并没有关闭,但是客户端 -----> 服务端已经关闭);
  4. 当客户端收到服务端的确认报文时,就会入FIN-WAIT2(终止等待2)状态,此时此刻客户端就会等待服务端发出来的释放连接报文段,其实这一步即使确定服务端要给客户端的数据发送完毕
  5. 若服务端已经没有数据再给客户端发送了的时候,服务端应用进程就会通知服务端TCP释放连接。这是服务端向客户端发送连接释放报文:FIN=1,ACK=1,seq=w(在半关闭状态服务端又给客户端发送了一些消息),ack=u+1;注意: 规定ACK报文可以携带数据,但如果不携带数据就不消耗序号; 在发送完释放连接报文后服务端处于LASK-ACK(最后确认)状态
  6. 在客户端收到服务端的释放连接报文后,必须对此作出确认回应。即给服务端发送确认报文:ACK=1,ack=w+1,seq=u+1;注意:前面客户客端发送FIN包消耗一个序号;发送完确认报文后客户端处于TIME-WAIT(时间等待)状态 此时TCP的链接并没有关闭,还要经过时间等待计时器(TIME-WAIT timer) 设置的时间2MSL后,客户端才进入CLOSED状态,关闭客户端,完成这次连接;
  7. MSL(最长报文段寿命) 规定MSL=2分钟,但是也允许进行修改MSL;
  8. 在服务端收到客户端的确认报文后,服务端就进入CLOSED状态,然后服务端完成后结束这次连接;
  9. **注意:**可以看到服务端先于客户端结束连接;

问题:

  1. 为什么客户端在TIME-WAIT(时间等待)状态 必须等待2MSL的时间呢?

解答:
1、为了保证客户端左后一次发送的确认报文能够到达服务端;因为左后一个ACK报文段可能会丢失,这样处于LASK-ACK(最后确认)状态的服务端就可能收不到来自客户端的确认报文,而这时服务端会超时重传一个FIN+ACK的报文段,而这就保证了客户端能在2MSL时间内面能够收到服务端重发的报文段,接着客户端再一次进行确认,重新启动2MSl计时器,最后客户端和服务端都能进入CLOSED状态。那么如果客户端进行了确认后,不等待直接退出,那么就收不到服务端重发的释放连接报文段,那么也就不会再一次发送确认报文,那么服务端就没办法正常进入CLOSED状态
2、防止已失效的链接请求报段出现在本连接中,客户端在发送完最后一个确认报文段后,在经过2MSL,就可以保证本连接持续时间内所产生的的所有报文段都从网络中消失;
3、若客户端直接退出,此时有新的客户端使用相同的地址信息启动;此时前边的服务端没有收到ACK确认报文,向客户端重发发送FIN报,但是此时客户端不是上一个客户端里只不过二者使用的相同地址信息,但是前面的那个客户端一个退出;这就对新连接的客户端造成了影响;若此时新客户端向服务端发送连接请求,会被服务端误认为上一个客户端发送请求任务将其认作错误状态,重新客户端的链接;

  1. 服务端主机上,出现了大量的TIME-WAIT状态连接,为什么?怎么办?

解答:
1、调整TIME-WAIT等待时间
2、使用接口函数int setsockopt(int fd, int level,int name,void * options,int len);
level:SOL_SOCKET—套接字选项设置
name:SO_REUSEADDR—地址重用(重新使用星通的地址)选项
options:开启/关闭选项 option=1;
len:options数据的长度

  1. 连接在代码中怎样体现?

解答:
1、recv返回0
2、send触发SIGPIPE异常

TCP保活机制:TCP内面有一个:保活计时器
有一个场景客户端和服务端已经建立了连接,但是客户端出现故障,显然服务器不可能在收到客户端到的信息了,因此就有必要措施不能让服务器白白等待,这时就要使用保活计时器,服务器每收到一次客户端的小心就会重置一次保活计数器,时间设置为2小时,,若超过2小时没有收到客户端信息,服务器就会发送探测报文段,每隔75分钟发一次,若连发了10次客户端无响应,服务端认为客户端出现故障,然后关闭服务端;