zoukankan      html  css  js  c++  java
  • TCP 三次握手四次挥手

    TCP 的基本信息

    • 序列号:在建立连接时,由计算机随机生成的随机数,作为其初始值,通过SYN 包传给接收端的主机,每发送一次数据,就累加一次该数据字节数大小,用来解决网络包乱序问题。
    • 确认应答号:指下一次期望收到的数据的序列号,发送端收到这个确认应答以后,可以认为在这个序号之前的数据都被正常接收,用来解决不丢包问题。
    • 控制位:ACK 该位为1时,确认应答的字段变为有效,TCP 规定除了最初建立连接的SYN包之外,该位必须设置为1。 RST为1,表示TCP连接中出现异常必须强制断开连接。 SYN:该位为1时,表示希望建立连接,在其序列号的字段进行序列号的初始值的设定,FIN 为1时,表示今后不会再有数据发送,希望断开连接。
      IP 层是不可靠的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果要保证网络数据包的可靠性,那么就需要上层的TCP协议负责。他保证接收端接受的网络包时无损坏、无间隔、非冗余和按序的。TCP的链接是字节流形式,无论我们消息有多大都可以进行传输,并且消息是有序的,对前一个消息没有接受的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对重复的报文会自动丢弃。

    建立一个TCP 连接时需要客户端以及服务端达成上述的三个信息的共识:

    • Socket: 由IP 地址和端口号组成
    • 序列号:用来解决乱序问题
    • 窗口大小: 用来做流量控制

    建立一个TCP四元组包括如下:

    • 源地址
    • 源端口
    • 目的地址
    • 目的端口

    一旦完成三次握手,双方都处于ESTABLISHED状态,此时连接就已经建立完成了,客户端和服务端就可以相互发数据了。

    为什么是三次握手?不是两次、四次?

    • 避免历史连接
      三次握手的首要原因就是为了防止旧的重复连接初始化造成混乱。
    • 同步双方初始序列号
      序列号是可靠传输的一个关键因素,他的作用是:1、接受方可以去除重复数据,2、接受方可以根据数据包中的序列号按序接受 3、可以标识发送出去的数据包,那些是已经被对方收到的。

    避免资源浪费

    如果客户端的 SYN 阻塞了,重复发送多次SYN 报文,因为服务器不清楚客户端是否收到了自己发送的建立连接的ACK 确认信号,所以每收到一个SYN,就只能先主动建立一个连接。这样造成了资源的浪费。

    为了达到最佳的传输效能,TCP 协议在建立连接的时候通常需要协商双方的MSS值,当TCP层发现数据超过了MSS时,则就会先进行分片,当然由它形成的IP包的长度也就不会大于MTU, 自然也就不会进行IP 分片了。经过一个TCP 分片之后,进行重发的时候是以MSS为单位,而不用重传所有的分片,大大增加了重传的效率。

    SYN 攻击

    TCP 建立连接时需要三次握手的,假设攻击者短时间内,伪造了不同的IP 地址的SYN 报文,服务端每接受到一个SYN 报文就进入SYN_RCVD 状态,但是服务端发送出去的ACK + SYN 报文,无法得到未知IP 主机的ACK应答,久而久之就会沾满服务端的SYN 接受队列,使得服务器不能为正常用户服务。
    LINUX 内核SYN(未完成连接建立) 队列与Accpet(以完成连接建立)队列是如何工作的?

    • 当如无端接收到客户端的SYN 报文时候,将其加入到内核的SYN 队列
    • 接着发送SYN + ACK给客户端,等待客户端回应ACK 报文。
    • 服务端接收到ACK 报文后,从SYN 队列移除到Accept 队列。
    • 应用通过调用accpet() socket 接口,从 Accept 队列中取出连接。

    TCP 连接的断开

    只有主动关闭连接的,才会有TIME_WAIT状态
    需要TIME_WAIT状态主要是两个原因:

    • 防止旧的连接的数据包,经过2MSL这个时间,足以让两个方向上的数据包都被丢弃,使原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
    • 等待足够的时间保证最后的ACK能让被动关闭方接受,从而帮助其正常关闭。
      TIME_WAIT 状态主要的危害有两种:
    • 对内存资源占用
    • 对端口资源的占用,一个TCP 连接至少消耗一个本地端口。

    Socket 编程

    • 服务端和客户端初始化socket,得到文件描述符
    • 服务端调用 bind 将绑定在IP地址和端口
    • 服务端调用 listen, 进行监听
    • 服务端调用accept, 等待客户端连接
    • 客户端调用connect, 向服务器端的地址和端口发起连接请求
    • 服务器端调用accept 返回用于传输的 socket 文件描述符;
    • 客户端调用 write 写入数据; 客户端调用read 读取数据;
    • 客户端断开连接,会调用close, 那么服务端 read 读取数据的时候,就会读取到EOF, 待处理完数据之后,服务端会调用close, 表示连接关闭。

    注意的是当服务端调用accept 时,连接成功了就会返回一个已完成的socket,后续用来传输数据,所以监听的socket 和真正用来传送数据的socket 时俩socket,一个作监听socket, 一个叫做已完成连接socket

    • LINUX 内核中会维护两个队列
      未完成连接队列(SYN队列):接到一个SYN建立连接请求,处于SYN_RCVD 状态
      已完成的连接队列(Accept队列): 已经完成了TCP的三次握手过程,处于ESTABLISHED 状态。

      客户端的connect 成功返回时在第二次握手,服务端accept 成功返回时在第三次握手
  • 相关阅读:
    Anaconda+Vscode+opencv3环境打造
    关于AXI4-Stream Infrastucture IP的学习
    Xilinx的License问题
    IOBUF的使用
    Vscode的学习
    关于ZYNQ 7 processing system IP的设置导出与导入
    使用zynq verification IP进行系统验证
    使用AXI Verifcation IP进行系统验证
    MIG IP学习笔记
    Git的GUI工具sourcetree的使用
  • 原文地址:https://www.cnblogs.com/wsl-hitsz/p/14544580.html
Copyright © 2011-2022 走看看