码迷,mamicode.com
首页 > 其他好文 > 详细

TCP协议的通讯流程

时间:2021-05-24 01:03:52      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:相同   相对   add   write   为什么   情况下   port   inf   发送数据   

TCP协议的通讯流程

基于TCP/IP协议的服务器和客户端程序的一般流程,如下图所示:
技术图片

1、服务器初始化——LISTEN
(1)调用socket函数创建文件描述符。
(2)调用bind函数将当前的文件描述符和ip/port绑定在一起。如果这个端口已经被其他进程占用了,就会bind失败。
(3)调用listen函数声明当前这个文件描述符作为一个服务器的文件描述符,为accept做好准备。
(4)调用accept函数阻塞等待客户端连接起来。

2、建立连接的过程——三次握手(绿色部分)
第一次:调用connect函数发出SYN段向服务器发起连接请求,并阻塞等待服务器应答。
第二次:服务器收到客户端的SYN段后,会应答一个SYN-ACK段表示“同一建立连接”。
第三次:客户器端收到SYN-ACK后会从connect函数中返回,同时应答一个ACK段。

3、数据传输的过程(蓝色部分)
建立连接后,TCP协议提供全双工的通信服务。所谓全双工,意思是:在同一条链路中的同一时刻,通信双方可以同时写数据。相对的概念叫做半双工,即:在同一条链路中的同一时刻,只能由一方来写数据。
(1)服务器从accept函数返回后立刻调用read函数读socket里的数据。读socket就像读管道一样,如果没有数据到达就阻塞等待。
(2)客户端调用write函数发送请求给服务器,服务器收到后就向客户端回复ACK,并从read函数中返回,对客户端的请求进行处理。在此期间客户端调用read函数阻塞等待服务器的应答。
(3)服务器调用write函数将处理结果发回客户端,客户端收到后就回复ACK。服务器再次调用read函数阻塞等待下一条请求,。
(4)客户端从read函数中返回,并发送下一条请求,如此循环下去。

4、断开连接的过程——四次挥手(红色部分)
第一次:如果客户端没有更多的请求就调用close函数关闭连接,客户端会向服务器端发送FIN端。
第二次:服务器收到FIN后会回应一个ACK,同时read返回0。
第三次:客户端收到FIN后,再返回一个ACK给服务器。

以上过程,基于TCP/IP 协议的套接字的工作流程,如下图所示:

技术图片

5、问题
(1)为什么要三次握手而不是两次?
答:这主要是为了防止已失效的的连接请求报文段。
假设,客户端发出连接请求报文段,但是该连接请求报文段 在某个网络节点中长时间滞留,导致客户端因为超时又发送第二个连接请求报文段 。第二个连接请求与服务器建立连接,而第一个连接请求报文段 以至于延误到连接释放以后才到达服务器。本来这个连接早已经失效了,如果只采用两次握手的话,服务器会误认为客户端又发送了一次连接请求,从而统一建立连接。所以,采用三次握手可以很好的避免这个问题。

(2)为什么连接的时候是三次握手,断开的时候是四次挥手?
答:因为连接的时候,服务器收到客户端的SYN连接报文后可以直接发送SYN+ACK报文(其中SYN报文是用来同步的,ACK报文是用来应答的);但是,断开的时候,服务器收到客户端的FIN报文后并不会立即关闭SOCKET,而是先回复一个ACK报文,告诉客户端“我收到你发的FIN报文了”,直到服务器的所有报文都发送完了,才发送FIN报文,因此不会一起发送FIN+ACK。故需要四步握手。

(3)为什么有时候是三次挥手?
答:因为假设在服务器发送ack给客户端的时候,服务器端已经没有要发送的数据,则这时服务器会将ACK捎带在第二次挥手的FIN报文里面返回回去,也就是第二和第三合在一起发送了。

(4)为什么TIME_WAIT的时间是2MSL?
答:因为MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失,否则服务器立刻重启,可能会收到来自上一个迟到的数据而引起错误。同时,也是在理论上保证最后一个报文可靠到达。假设最后一个ACK丢失,那么服务器会重发一个FIN。这时,虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK。

(5)服务器关闭之后为什么不能立即重启?
答:这是由于套接字处于TIME_WAIT状态引起的,这个状态会持续2MSL时间。在TIME_WAIT退出后,套接字被删除,该地址才能被重新绑定而不出现问题。

6、注意:
(1)中断连接可以是客户端,也可以是服务端。
假设由客户端中断连接,即客户端发起FIN报文中断连接请求,告诉服务器“我没有数据发给你了”。这时,客户端进入FIN_WAIT_1状态。服务器收到FIN报文后,如果还有数据没有发送完成,则不会急着关闭SOCKET,会继续发送数据。这时,服务器会发送ACK告诉客户端“我收到你的断开请求了,但是我还没有准备好,请等我消息”。这时,客户端就进入FIN_WAIT_2状态,继续等待服务器的FIN报文。当服务器确认数据已经发送完成,则向客户端发送FIN报文,告诉客户端“好了,我这边的数据发完了,准备断开连接”。客户端收到FIN报文后就知道断开连接,但是它不相信网络,怕服务器不知道断开,所以发送ACK后进入TIME_WAIT状态。如果服务器没有收到ACK报文,则客户端会继续重传ACK,服务器收到ACK报文后就知道可以断开连接了。客户端等待2MSL后依然没有收到回复,则证明服务器已正常关闭。那好,我客户端也可以关闭连接了。OK,TCP连接就这样关闭了。

(2)在服务器的TCP连接没有完全断开之前不允许重新监听,某些情况下可能是不合理的。
【例】
服务器需要处理大量的客户端的连接,每个连接的生存时间可能很短,但是每秒都有很多客户端来请求。这个时候,如果由服务器主动关闭连接,比如某些客户端不活跃,就需要被服务器主动清理掉,这时会产生大量的TIME_WAIT连接。由于我们的请求量很大,就可能导致TIME_WAIT的连接数很多而导致服务器的端口不够用,无法处理新的连接。

(3)如何解决TIME_WAIT状态引起的bind失败?
答:使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同,但是IP地址不同的多个socket描述符。即,在server代码的socket函数和bind函数调用之间插入如下代码:
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

TCP协议的通讯流程

标签:相同   相对   add   write   为什么   情况下   port   inf   发送数据   

原文地址:https://www.cnblogs.com/jgg54335/p/14743298.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!