标签搜索

目 录CONTENT

文章目录

TCP协议通讯流程(三次握手及四次挥手).md

小小城
2021-08-22 / 0 评论 / 0 点赞 / 5 阅读 / 4,208 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-02,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

TCP协议通讯流程(三次握手及四次挥手)

@[toc]

一、服务器端

  1. 调用socket函数,创建一个socket(文件描述符)操作句柄
  2. 调用bind函数,把刚创建的socket文件描述符 和 ip及端口 绑定在一起,注意一个进程可以绑定多个端口号,但是一个端口号只能被一个进程绑定bind中进程和端口的关系
  3. 调用listen函数,对socket文件描述符进行监听(首先listen会在进程地址空间中维护一个缓冲区,如果有客户链接进来,就把其放进缓冲区)
  4. 进入主循环,循环处理客户端的请求(死循环)
  5. 调用accept函数并阻塞,等待客户端进来链接(在listen缓冲区中去出),accept成功就返回一个新的socket文件描述符操作句柄,在多进程或多线程等情况下,旧的socket在父进程中返回,继续accept,子进程拿到newsocket对客户进行操作(下面的操作都是newsocket)
  6. 调用recv函数,接受客户端发送的请求
  7. 对客户端发送来的请求在函数内部进行处理
  8. 调用send函数,把处理结果返回给客户端。
  9. 关闭socket

二、客户端

  1. 调用socket函数,创建一个socket(文件描述符)操作句柄
  2. 调用connect函数,向服务器发送链接请求
  3. 进入主循环,服务器和客户端可能进行多次交互,不止一次
  4. 调用send函数,把请求数据发送给服务器
  5. 调用recv函数,接受服务器返回的结果
  6. 关闭socket

三、三次握手四次挥手

  •  1 . 三次握手

  • 在客户端向服务器建立链接的过程中:客户端首先创建自己的socket

  • 调用connect发起链接服务器的请求

  • 其中connect函数会发出SYN段并阻塞等待服务器的响应 ==(第一次)==

  • 服务器收到客户端的链接请求SYN,会应答客户端,向客户端发送一个SYN-ACK,表示同意建立连接请求 ==(第二次)==

  • 客户端收到服务器端的SYN-ACK后会从connect返回,同时向服务器端再应答一个ACK段; ==(第三次==

  •  2.四次挥手

  • 在数据传输的过程中:建立连接后,TCP协议提供全双工的通信服务(全双工:在同一条链接中,同一时刻通信双方都可以写数据)。

  • 服务器在调用socket创建出socket文件描述符后,再调用bind绑定ip及端口号,服务器在调用accept获取客户端,调用完accept后立即调用recv函数(阻塞,跟read读取管道一样)

  • 在这个时间客户端调用send函数向服务器端发送请求(和write管道一样),服务器收到从recv返回的请求,在对客户端的请求进行处理,在这期间客户端调用send函数阻塞等待

  • 服务器端调再用send把响应结果发给客户端,再次调用recv读取客户端请求

  • 在断开链接的过程中(一般都是客户端主动断开链接,服务器毕竟不是为一个客户服务):如果客户端不在请求了,就会调用close函数关闭链接,客户端就会向服务器端发送一个FIN段,表示我要断开链接了 ==(第一次)==

  • 此时服务器端在响应完客户端的请求后,没有在收到客户端的请求,一直阻塞在recv处,此时收到客户端发送的FIN后,会向客户端回应一个ACK,同时recv会返回0 ==(第二次)==

  • 服务器端recv返回后,服务器端就直到客户端要关闭链接了,也调用close函数关闭连接,再向客户端发送一个FIN ==(第三次)==

  • 客户端收到ACK和FIN后,再向服务器端发送一个ACK ==(第四次)==

三、具体流程

在这里插入图片描述

  •  ==服务端状态转化==:
  • [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态, 等待客户端连接;
  • [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段), 就将该连接放入内核等待队列中, 并向客户端发送SYN确认报文.
  • [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文ACK, 就进入ESTABLISHED状态, 可以进行读写数据了
  • [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close), 服务器会收到结束报文段, 服务器返回确认报文段ACK并进入CLOSE_WAIT;
  • [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据); 当服务器真正调用close关闭连接时, 会向客户端发送FIN, 此时服务器进入LAST_ACK状态, 等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  • [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK, 彻底关闭连接
  •  ==客户端状态变化==
  • [CLOSED -> SYN_SENT] 客户端调用connect, 发送同步报文段;
  • [SYN_SENT -> ESTABLISHED] connect调用成功, 则进入ESTABLISHED状态冰箱服务器端发送一个ACK, 开始读写数据;
  • [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时, 向服务器发送结束报文段FIN, 同时进入FIN_WAIT_1;
  • [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认, 则进入FIN_WAIT_2, 开始等待服务器的结束报文段;
  • [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出LAST_ACK;
  • [TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(MaxSegment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态

四、相关注意事项

  •  1 . 客户端为什么不需要bind?

  • 首先服务器端bind是因为如果客户端要访问服务器端,就必须要服务器端端“地址”,也就是ip+端口号,这样客户端才能找到服务器,进行通信

  • 如果服务器端不进行bind操作,那么操作系统就会随机分配端口号给服务器,那么客户端就不能和服务器端进行通信。

  • 而客户端为什么不需要bind,是因为如果客户端bind一个ip + 端口,那么如果一个pc上有多个用户,那么bind同一个端口就会出错(如果一台pc上只有一个用户也是可以bind)

  •  2 . 为什么要进行三次握手?

  • 为了防止无效的连接请求报文到达服务器而引起错误。

  • TCP发起建立连接的一方不会一直等待对方的回复,如果超时,他再次发起这个请求,上一个作废。

  • 假设A给服务器发送了一个请求,但是由于网络原因迟迟没有到达B服务器。由于一直没收到服务器端的回复确认,所以就进行超时重传,上个就舍弃了。

  • 然后新的请求很快的到达了B服务器,然后B服务器也很快的进行了响应,如果是两次握手的话,这样就建立了连接

  • 但是上次因网络问题迟迟未到的第一个请求这时到达了B服务器,服务器依然会当成新的连接请求进行响应,(服务器只要响应,第二次握手就完成了)这时又会建立连接,这就会出现建立了两个连接的局面,

  • 然后这就会出现很多问题,例如服务器端认为完成了握手,可以发送数据了,于是一直处于等待数据状态,而发送端不理睬服务器端发来的请求(因为发送端的那个请求早就被清除了),不去发送数据,后果就是服务器一直等,这样就会浪费很多服务器资源

  • 如果是三次握手的话,就会避免这个问题,因为比如第二次的新请求到达时,发送端就会又给服务器端发送个确认请求,表示接收到了,这样迟到的那个请求到达服务器端,然后返回到发送端时,发送端并不会响应。然后这个连接并不会建立,服务器也就不会白白等待。

  •  3.为什么要进行四次挥手?

  • 确保数据能够完整传输。

  • 当被动方收到主动方的FIN报文通知时,它仅仅表示主动方没有数据再发送给被动方了。

  • 但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET,它可能还需要发送一些数据给主动方后,(按照常理的话,第二次和第三次挥手应该一起回复FIN和ACK的,但是因为服务器端可能有数据没发完,所以不能也立刻去主动申请关闭,所以要把ACK和FIN分开)再发送FIN报文给主动方,告诉主动方同意关闭连接,所以这里的ACK报文和FIN报文多数情况下都是分开发送的。

  •  4.timewite的产生的原因及作用?

  • 为实现TCP全双工连接的可靠释放

  • 假设发起主动关闭的一方(client)最后发送的ACK在网络中丢失,由于TCP协议的重传机制,执行被动关闭的一方(server)将会重发其FIN

  • 在该FIN到达client之前,client必须维护这条连接状态,也就说这条TCP连接所对应的资源(client方的local_ip,local_port)不能被立即释放或重新分配

  • 直到另一方重发的FIN达到之后,client重发ACK后,经过2MSL时间周期没有再收到另一方的FIN之后,该TCP连接才能恢复初始的CLOSED状态。

  • 如果主动关闭一方不维护这样一个TIME_WAIT状态,那么当被动关闭一方重发的FIN到达时,主动关闭一方的TCP传输层会用RST包响应对方,这会被对方认为是有错误发生,然而这事实上只是正常的关闭连接过程,并非异常

  • 确保第四次握手时发送方发送的ACK可以到达接收方 如果2MSL时间内没有收到,则接收方会进行重发

  • 确保当前连接的所有报文都已经过期(每个具体TCP实现必须选择一个报文段最大生存时间MSL。它是任何报文段被丢弃前在网络内的最长时间,2MSL足够使所有报文过期了)

  •  5.理解close_wait状态?

  • close_wait状态介绍:

  • 客户端主动关闭连接,服务器接收到客户端的FIN,但是还没有发送自己的FIN,此时的状态为close_wait状态,大量的close_wait状态拖累服务器性能

  • close_wait产生的原因:

  • 某种情况下客户端关闭了连接,但是我方忙于读写,没有关闭连接

  • 解决方法:

  • 思想:检查出客户端已经关闭的连接,他之所以会出现这种问题,肯定是服务器端的连接释放的代码存在问题

1.当服务器读写失败时,可以选择关闭连接

2.定期向连接发送询问数据,检查收到的回复数据包(Heart-Beat线程发送指
定格式的心跳数据包)

3.修改keep-live参数(超时时间,tcp检查间隔时间:keeplive探测包
发送的间隔,tcp检查次数:如果对方不予应答,探测包发送的次数)
0

评论区