zoukankan      html  css  js  c++  java
  • 理解TCP中的三次握手和四次分手

    1、前言

    之前上过计算机网络这门课,由于当时初次接触计算机网络,其中的有些概念无法深入理解,只停留在表面。这次借着学网络编程的机会,也把TCP的三次握手和四次分手重新梳理了一遍,有了不同的理解。借此,想做一个总结。

    2、TCP协议

    在学习TCP三次握手和四次分手之前,首先得对TCP协议有一个大概的了解。TCP全称是传输控制协议,其是面向连接的,可靠的,基于字节流的传输层通信协议。相比与UDP(用户数据报协议)而言,具有以下几个特点:

    • TCP协议是面向连接的。基于TCP协议,客户端和服务端要想传输数据,两者之间要先建立一个连接,然后客户端再跨该链接与服务端交换数据,最终终止连接。
    • TCP提供可靠性。当TCP的一端向另一端传输数据之后,要求收到数据的一端必须要返回一个确认,如果没有收到这个确认,那么发送端会自动重传数据病等待更长时间,数次失败之后才会放弃(4-10分钟)。TCP提供的是可靠传输,但并不保证数据一定会被对方给接收到。如果多次重传都失败的话,TCP就会放弃重传并通知用户。
    • TCP会给传输数据中的每一个字节关联一个序列号对所发送的数据进行排序。这样可以接受到的数据和发送的数据顺序保持一致并会包含重复数据。
    • TCP提供流量控制。TCP总是会告诉对方任何时刻它一次能接受多少字节的数据(通告窗口),从而确保发送端发送的数据不会使接受缓冲区溢出。通告窗口随时会发生改变,当接收到了新的数据,通告窗口减少,当应用程序从缓冲区读取新的数据时,通告窗口会增大。
    • TCP是面向全双工的。一个给定连接的应用在任何时刻既可以接收数据也可以发送数据。

    3、三次握手

    三路握手

    三次握手

    1. 服务器调用socket,bind,listen这三个函数来准备接收外来的连接(被动打开)。
    2. 客户端调用connect发起主动打开。这样客户端会给服务器发送一个SYN分节,告诉服务器将要建立连接中发送数据的起始初始化序列号J。SYN不携带数据,但包含部分TCP选项。
    3. 服务端发送一个ACK J+1返回期望收到的下一个序列号,以达到告诉客户端已经收到SYN的作用,同时自己也发送一个SYN 分节,其含有服务器将在同一连接中发送的数据的初始化序列号K。服务器在单个分节中发送SYN K和对客户端SYN的ACK J+1确认。
    4. 客户端收到来自服务器的ACK和SYN之后,发送一个ACK K+1确认。

    为什么需要三次握手,两次握手不行吗?

    三次握手的作用是为了解决网络中延迟的重复分组。引用谢希仁著《计算机网络》书中的一个例子:

    “已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。
    本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,
    那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,
    并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,
    就知道client并没有要求建立连接。”
    

    4、四次分手

    TCP终止

    1. 某个应用程序调用close函数,主动关闭连接。该端发送一个FIN分节,表示数据发送完毕。
    2. 接收到FIN的对端执行被动关闭。这个FIN由TCP发送一个ACK确认,同时这个FIN作为一个文件结束符传递给接收端应用程序(放在已排列等候该应用程序接收的任何其他数据之后),FIN表示接收端应用进程在相应连接上没有额外数据可以接受。
    3. 一段时候后,当应用程序接收到文件结束符之后将调用close函数关闭它的套接字,同时TCP发送一个FIN。
    4. 接收到这个最终FIN的原发送端TCP确认这个FIN。

    注意

    • 当客户端调用close函数之后意味客户不再会有数据发送给服务端,服务端也不会接受额外的数据。但服务器端没有调用close函数之前,服务器端是可以再向客户端发送数据的,这称为半关闭。

    • 一个应用进程发送FIN,除了程序自己主动调用close函数之外,如果进程自愿或者被动的终止时,所有打开的描述符都会被关闭,这也会导致TCP发送一个FIN。

    TCP状态转换图

    放一张TCP状态转换图便于理解。

    状态转换

    再放一张完整的TCP连接所发生的实际分组交换情况。

    分组

    在上面的所有状态中,最值得我们注意的两个状态是CLOSE_WAIT状态和TIME_WAIT状态。

    CLOSE_WAIT状态解释

    当服务端接收到客户端发来的FIN分节时,服务端的状态由ESTABLISHED转变为CLOSE_WAIT状态,并返回一个ACK分节确认,同时在服务端的接收缓冲区中添加一个文件结束符。此时客户端将不再给服务端发送数据,但服务端的缓冲区数据可能还没有被应用程序接收完,所以此时服务端的程序暂时还不能关闭。当服务端的程序将缓冲区的数据读取完时(遇到文件结束符),将给客户端发送一个FIN分节,服务端进入LAST_ACK状态。客户端接收到FIN,由FIN_WAIT状态进入到TIME_WAIT状态。

    TIME_WAIT状态解释

    端点停留在TIME_WAIT状态的持续时间是最长分节生命期的两倍,即2MSL。

    作用

    1. 可靠的实现TCP全双工连接的终止。
    2. 允许老的重复分节在网络中消失。
  • 相关阅读:
    linux基础_用户和组的三个文件
    python_文件
    linux基础_用户组的管理
    mysql基础_数据类型
    mysql基础_操作数据库、表、记录
    linux基础_用户管理
    python_集合
    linux基础_关机重启注销
    docker创建私有仓库
    制作docker镜像
  • 原文地址:https://www.cnblogs.com/zrcsy/p/12355415.html
Copyright © 2011-2022 走看看