TCP协议的通讯流程

本文主要讲的是TCP连接机制及其过程,TIME_WAIT状态详解,为什么要进行三次握手和四次挥手。
一、TCP连接管理机制
在这里插入图片描述
服务器的初始化

  • 调用socket,创建文件描述符。
  • 调用bind,将当前的文件描述符和ip/port绑定在一起;如果这个端口已经被其他进程占用了,就会bind失败。
  • 调用listen,声明当前这个文件描述符作为一个服务器的文件描述符,为后面的accept做好准备。
  • 调用accept,并阻塞,等待客户端连接过来。

建立连接的过程:

  • 调用socket,创建文件描述符。
  • 调用connect,向服务器发起连接请求。
  • connect会发出SYN字段并阻塞等待服务器应答–(第一次)。
  • 服务器收到客户端的SYN,会应答一个SYN+ACK字段表示"同意建立连接"–(第二次)。
  • 客户端收到SYN+ACK后会从connect()返回。同时应答一个ACK字段–(第三次握手)。

这个建立连接的过程,通常称为三次握手.

数据传输的过程

  • 建立连接后,TCP协议提供全双工的通信服务;所谓全双工的意思是,在同一条连接中,同一时刻,通信双方可以同时写数据;相对的概念叫做半双工,同一条连接在同一时刻,只能由一方来写数据。
  • 服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待;
  • 这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答。。
  • 服务器调用write()将处理结果发回给客户端,在次调用read()阻塞等待下一条请求。
  • 客户端收到后从read()返回,发送下一条请求,如此循环下去。

断开连接的过程

  • 如果客户端没有更多的请求了,就调用close()关闭连接,客户端会向服务器发送FIN字段–(第一次)。
    -此时服务器收到FIN后,会回应一个ACK,同时read()会返回0–(第二次).
  • read返回之后,服务器就知道客户端关闭了连接,也调用close关闭连接,这个时候服务器会向客户端发送一个FIN–(第三次)。
  • 客户端收到FIN,在返回一个ACK给服务器–(第四次)。

这个断开连接的过程,通常称为四次挥手。

服务器的状态转化:
1、CLOSED->LISTEN:服务器创建监听套接字后进入LISTEN状态,等待客户端建立连接;
2、LISTEN->SYN_RCVD:当监听到建立连接请求(SYN),就将该连接放入到内核等待队列,并给客户端发送ACK确认;
3、SYN_RCVD->ESTABLISHED:当再次受到客户端的ACK确认报文,就建立一个新链接,进入WSTABLISHED状态,此时可以读写数据;
4、ESTABLISTED->CLOST_WAIT:当客户端主动断开连接,服务器收到FIN结束报文段,此时服务器发送ACK确认报文,并进入CLOSE_WAIT状态;
5、CLOSE_WAIT->LAST_ACK:处理完之前的数据,服务器就调用close关闭连接,并发送FIN结束报文段,进入LAST_ACK状态,等客户端发送最后一个ACK确认;
6、LAST_ACK->CLOSED:收到了客户端对结束报文段的ACK确认,彻底关闭连接。

客户端的状态转化:
1、CLOSED->SYN_SENT:开始connect,向服务器发送SYN同步报文段;
2、SYN_SENT->ESTABLISHED:收到了服务器的SYN同步报文段和ACK确认,则客户端认为连接建立好了,此时进入ESTABLISHED状态,可以读写数据;

如果三次握手时,最后一次报文丢失,客户端会认为连接建立好了,
然而服务器认为没有建立好链接,此时服务器会给客户端一个RST响应,
告诉客户端并没有建立好链接。当客户端收到该响应:应该重新建立连接或者关闭连接。

3、ESTABLISHED->FINWAIT1:客户端主动调用close时,向服务器发送结束报文段,并进入FINWAIT状态;
4、FINWAIT1->FINWAIT2: 当收到服务器对结束报文的ACK确认,客户端进入FINWAIT2状态,等待服务器的FIN结束报文段;
5、FINWAIT2->TIME_WAIT:当客户端收到服务器的结束报文段,会进入TIME_WAIT状态,并向服务器发送最后一次ACK确认了报文结束;
6、TIME_WAIT->CLOSED:客户端会等待2MSL(两个报文生存最大时间),才进入CLOSED彻底关闭;

二、TIME_WAIT状态详解
首先来做一个实验,验证TIME_WAIT是存在的:
这个实验是基于一个网络版本的聊天工具,先启动服务器,在启动客户端:
在这里插入图片描述
在这里插入图片描述
之后我在终止客户端,然后在运行客户端,发现除了一些问题:
在这里插入图片描述
而问题的原因是虽然服务器被终止了,但TCP连接并没有完全断开,主动断开的一方进入TIME_WAIT状态,等待两个MSL(报文生存最大时间)后才能回到CLOSED状态,此时8889这个端口还在被占用。

TIME_WAIT的原因:
举个例子:我是商学院的学生,商学院通知明天早上7点在二教201教室开会,到了第二天,我因为一些原因,到了指定的教室已经是早上十点了,这时候已经是别的学院在这个教室开会了。
所以,院长决定,等一段时间,等迟到的人都到了,在结束会议。

1、等待两个报文的最大生存时间,就能保证两个传输方向上没有被接收或迟到的报文段消散;
2、保证最后一次ACK可靠到达(因为最后一次ACK可能丢失,主动断开连接的一方要等待;如果对方重发了FIN,则最后一次ACK丢失;若没收到,则发送成功);

如何解决因为TIME_WAIT引起的bind失败
在一些场景下,服务器的TCP连接在没有完全断开之前不允许重新监听是不合理的;
服务器每秒都要处理大量的用户请求,但每个连接的生存时间都很短,此时如果某个客户端不活跃,被服务器主动断开连接(比如电脑登录qq,长时间不动),就会产生大量的TIME_WAIT状态,从而导致服务器的端口号不够用,无法建立新的连接。

解决方法:
在服务器代码的socket和bind之间,加上:

int opt = 1;                                                //端口复用
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

三、三次握手的原因
如果是两次握手,当服务器收到客户端发送的SYN同步报文字段之后,则认为连接建立,然后维护该连接,将该连组织管理起来,进入ESTABLISTED状态,并发送ACK给客户端;
客户端收到ACK后才会进入ESTABLISHED状态,建立连接,如果ACK丢包,客户端会认为连接没有建立,而重新向服务器发送重复的SYN报文段,服务器接收后会在次建立连接,而维护连接是消耗资源的,如果ACK一直丢失,客户端会一直发送SYN,服务器就会建立很对相同但无效的连接,从而使服务器的资源浪费,是服务器收到影响。

如果是三次握手,当客户端收到来自服务器的SYN+ACK后,进入ESTABLISHED状态,并建立和维护该连接;
客户端会发送ACK确认报文,服务器收到了才会进入ESTABLISTED状态,并且建立连接;
即使最后一次ACK丢失,客户端认为建好了链接,而服务器认为没有建好连接,此时服务器给客户端一个RST响应,告诉客户端没有建立连接。当客户端收到该响应;应重新建立连接或关闭老的连接。虽然客户端会建立一些无效的连接,会受到影响,但是服务器没有受到影响,从而保证了服务器的安全。同时虽然客户端收到了影响,但随后会重新建立连接,会减小收到的影响。

总结一下:
1、两次握手可能会使服务器建立很多无效的连接,浪费资源。
2、虽然三次握手可能会使客户端受到影响,但随后会重建。
3、三次就够了,不需要多次握手。

四、四次挥手的原因
四次挥手比较容易理解。就像情侣分手一样,分手是双方的事情,必须双方都同意。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值