美文网首页
网络基础知识学习(一)

网络基础知识学习(一)

作者: 刘景昌 | 来源:发表于2021-03-02 11:02 被阅读0次

    (1)在Tcp报文中需要了解的重要字段

    1.序号(sequence number):Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
    2.确认号(acknowledgement number):Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
    3.标志位(Flags):共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:
    URG:紧急指针(urgent pointer)有效。
    ACK:确认序号有效。
    PSH:接收方应该尽快将这个报文交给应用层。
    RST:重置连接。
    SYN:发起一个新连接。
    FIN:释放一个连接。

    (1)三次握手过程

    image.png

    1.首先客户端向服务器端发起一端TCP报文
    标志位 :SYN 发起一个新连接
    序号:seq =x
    随后客户端进入SYN-SEND阶段
    2.服务器端收到客户端发送的消息后 发送一段TCP报文进行响应
    标志位:ACK+SYN 表示确认客户端的seq有效,服务器能够正常接收客户端的数据,并同意创建新连接
    序号:序号为Seq=y
    确认号:ack = x+1 表示 把收到的序号+1 作为自己的确认号ack的值 并进入 SYN-RECV状态
    3.客户端收到服务端的确认消息,确认从客户端到服务器端的信息是正常的,结束SYN -SEND状态,然后给服务器返回一段TCP报文
    标志位:ACK,表示“确认收到服务器端同意连接的信号”(即告诉服务器,我知道你收到我发的数据了)
    序号:seq = x+1 表示 我收到的你的ack信息
    确认号:表示收到的服务器的seq值 并把他+1作为值得ack值 发送时候进入确认连接状态

    (2)为什么进行三次握手

    我们可以从三个方面来分析需要三次握手的原因
    1.三次握手才可以阻止重复历史连接的初始化(主要原因)
    2.三次握手才可以同步双方的初始序列号
    3.三次握手才可以避免资源浪费

    原因一 :三次握手才可以阻止重复历史连接的初始化

    我们来看看 RFC 793 指出的 TCP 连接使用三次握手的首要原因:
    The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.
    简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。网络环境是错综复杂的,往往并不是如我们期望的一样,先发送的数据包,就先到达目标主机,反而它很骚,可能会由于网络拥堵等乱七八糟的原因,会使得旧的数据包,先到达目标主机,那么这种情况下 TCP 三次握手是如何避免的呢?

    下面是一个在网络阻塞的情况下的Tcp 握手


    image.png

    1.在网络用的情况下客户端连续发送SYN报文
    2.旧的SYN报文 比新的SYN报文 先抵达服务器端
    3.服务器端对旧的报文进行响应 返回 对应的SYN+ACK报文
    4.客户端收到 服务器端的响应报文 与自己最新发送的报文 进行比对 发现并不是自己期望的报文 此时客户端会发送一个RST报文终止此次连接
    如果是两次握手 就没有判断当前连接时候为历史连接,三次握手 客户端 就是足够的信息来判断 当前连接是历史连接还是最新连接
    如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;
    如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;
    愿意二:三次握手才可以同步双方的初始序列号
    作为TCP协议的双方 ,都必须维护一[序列号],序列号是双方通信的关键因素,他的作用有

    • 接收方去除重复数据
    • 接收方可以根据序列号的顺序去接收
    • 可以标识发出去的数据包中那些已经被接收到了
      在第一握手的时候,客户端携带“初始序列号”的SYN报文,需要服务器的一个ACK报文应答报文,标识客户端的SYN已经被接收成功,当服务器发送一个“初始序列号”给客户端的时候客户端也需要给服务器端返回一个ACK表示我应答你了 ,就这样一来一回 就保证的初始序列号的可靠与同步

    愿意三:避免资源浪费
    如果只有两次握手,在客户的的SYN请求过程中发生网络阻塞,客户端就会一直发送SYN请求 ,此时服务器可能多次受到建立新链接的请求 ,然后建立重复连接

    即两次握手会造成消息滞留情况下,服务器重复接受无用的连接请求 SYN 报文,而造成重复分配资源。

    TCP四次挥手

    双方都可以主动断开连接,断开连接后主机中的「资源」将被释放。
    四次挥手的过程


    image.png

    1.客户端需要释放连接向服务器端发送一段TCP报文
    标志位:FIN
    序号:seq= u
    客户端进入FIN-WAIT-1阶段,即半关闭状态,并且停止客户端向服务端发送数据,但是客户端仍然可以接收到服务器返回的信息
    注意:这里不发送的是正常连接时传输的数据(非确认报文),而不是一切数据,所以客户端仍然能发送ACK确认报文。
    2.服务器端接收的客户端传输过来的报文后,确认客户端想要释放连接,随后服务器端返回一串报文并进入CLOSE-WAIT状态
    标志位:ACK
    序号:seq =v
    确认号:ack = u+1 表示是在收到客户端报文的基础上,将其序号Seq值加1作为本段报文确认号Ack的值
    随后服务器端开始准备释放服务器端到客户端方向上的连接。
    客户端收到从服务器端发出的TCP报文之后,确认了服务器收到了客户端发出的释放连接请求,随后客户端结束FIN-WAIT-1阶段,进入FIN-WAIT-2阶段、
    3.服务器端自送发送ACK确认报文后,经过CLOSED-WAIT阶段做好释放服务器端到客户端方向上的连接准备后,再次向客户端发出一段TCP报文,
    标志位:FIN+ACK
    序号:seq =w
    确认号:ack = u+1
    随后服务器结束CLOSE-WAIT阶段,进入LAST-ACK阶段,并且停止服务器到客户端发送数据,但是服务器仍然可以接收数据。
    4.客户端收到从服务器端发出的TCP报文,确认了服务器端已做好释放连接的准备,结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务器端发送一段报文,其中:
    标志位:ACK
    序号:seq = u+1
    确认号:ack = w+1
    随后客户端开始在TIME-WAIT阶段等待2MSL
    服务器端收到从客户端发出的TCP报文之后结束LAST-ACK阶段,进入CLOSED阶段。由此正式确认关闭服务器端到客户端方向上的连接。
    客户端等待完2MSL之后,结束TIME-WAIT阶段,进入CLOSED阶段,由此完成“四次挥手”。

    为什么 TIME_WAIT 等待的时间是 2MSL?

    MSL是 Maximum Segment Lifetime,报文最大生存时间,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃
    TIME_WAIT等待是2倍的MSL 是因为网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方接收后又向发送方发送响应,所以一来一回是2倍的MSL。

    在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。

    为什么需要 TIME_WAIT 状态?

    主动发起关闭连接的一方,才会有 TIME-WAIT 状态。
    需要TIME_WAIT状态主要有两个原因

    • 防止旧连接的数据包

    • 证最后的 ACK 能让被动关闭方接收,使「被动关闭连接」的一方能被正确的关闭;
      原因一:防止就连接的数据包
      我们假设一下没有TIME_WAIT或者TIME_WAIT很短 在被延迟的数据包抵达后会发生什么情况


      image.png

      即没有经过TIME_WAIT在形同TCP端口被复用后有接收到道曾经的301报文 可能会造成数据的混乱
      所以,TCP 就设计出了这么一个机制,经过 2MSL 这个时间,足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
      原因二:保证连接正确关闭
      TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭
      假设 TIME-WAIT 没有等待时间或时间过短,断开连接会造成什么问题呢?


      image.png
    • 如上图红色框框客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了,此时如果客户端 TIME-WAIT 过短或没有,则就直接进入了 CLOSE 状态了,那么服务端则会一直处在 LASE-ACK 状态。

    • 当客户端发起建立连接的 SYN 请求报文后,服务端会发送 RST 报文给客户端,连接建立的过程就会被终止。
      如果 TIME-WAIT 等待足够长的情况就会遇到两种情况:

    • 服务端正常收到四次挥手的最后一个 ACK 报文,则服务端正常关闭连接。服务端没有收到四次挥手的最后一个 ACK 报文时,则会重发 FIN 关闭连接报文并等待新的 ACK 报文。

    • 所以客户端在 TIME-WAIT 状态等待 2MSL 时间后,就可以保证双方的连接都可以正常的关闭

    TIME_WAIT 过多有什么危害

    危害一共有两个

    • 内存资源占用;
    • 对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;

    客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接。

    如何优化 TIME_WAIT?

    • 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;
    • net.ipv4.tcp_max_tw_buckets
    • 程序中使用 SO_LINGER ,应用强制使用 RST 关闭。
      方式一:net.ipv4.tcp_tw_reuse 和 tcp_timestamps

    如下的 Linux 内核参数开启后,则可以复用处于 TIME_WAIT 的 socket 为新的连接所用。有一点需要注意的是,tcp_tw_reuse 功能只能用客户端(连接发起方),因为开启了该功能,在调用 connect() 函数时,内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用。net.ipv4.tcp_tw_reuse = 1使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即net.ipv4.tcp_timestamps=1(默认即为 1)这个时间戳的字段是在 TCP 头部的「选项」里,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。由于引入了时间戳,我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。
    方式二:net.ipv4.tcp_max_tw_buckets

    这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的 TIME_WAIT 连接状态重置。这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。
    方式三:程序中使用 SO_LINGER我们可以通过设置 socket 选项,来设置调用 close 关闭连接行为。

    struct linger so_linger;
    so_linger.l_onoff = 1;
    so_linger.l_linger = 0;
    setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));
    如果l_onoff为非 0, 且l_linger值为 0,那么调用close后,会立该发送一个RST标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT状态,直接关闭。

    相关文章

      网友评论

          本文标题:网络基础知识学习(一)

          本文链接:https://www.haomeiwen.com/subject/yaeafltx.html