网络编程 socket programming 学习笔记 (一)

三次握手

TCP 三次握手 🤝

三次握手涉及的核心函数调用

Server::socket, bind, listen
Client::socket, connect

便于理解的一些解释

关于三次握手, 几句话解释清楚
1.信道不安全 保证通信需要一来一回
2.客户端的来回和服务端的来回 共四次 这是最多四次
3.客户端的回和服务端的来合并成一个,就是那个SYN k 和 ACK j+1
4.这样就是三次握手

为什么需要三次握手,不是四次?
tcp 的连接需要确保双方收和发消息的能力都是正常的。
客户端第一次发送握手 SYN 消息 j 到服务端, 服务端收到握手 SYN 消息 j 后把自己 的握手消息 SYN k 和 握手应答 ACK j+1 一并发送给客户端, 这是第二次握手,
当客户端收到服务端送来的第二次握手消息后,客户端可以确认“服务端的收发能力都OK,客户端的收发能力也OK”,
但是服务端只能确认“客户端的发送OK,服务端的接受OK”,
所以还需要第三次握手, 客户端收到服务端的第二次握手消息后,发起第三次ACK k+1 ,服务端收到第三次消息后,就能够确定“服务端的发送OK, 客户端的接收OK”,
至此,客户端和服务的都能够确认自己和对方的收发能力OK, tcp 连接建立完成。

四次挥手

流程概述

假设 发起端 主机A,接收端 主机B

  1. A 发送 FIN 报文 m , 进入FIN_WAIT_1
  2. B 回复 ACK 报文 m + 1 ,进入 CLOSE_WAIT 状态
  3. A 收到 ACK m + 1 , 进入FIN_WAIT_2
  4. 同时,B 通过read 调用获得 EOF,并将此结果通知应用程序进行主动关闭操作,发送 FIN 报文 n
  5. ​A 回复 ACK n + 1 ,进入 TIME_WAIT
  6. B 进入 CLOSED

通常在 TIME_WAIT 停留持续时间是固定的,是最长分节生命期 MSL(maximum segment lifetime)的两倍,一般称之为 2MSL。和大多数 BSD 派生的系统一样,Linux 系统里有一个硬编码的字段,名称为TCP_TIMEWAIT_LEN,其值为 60 秒。也就是说,Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。

四次挥手 🙋

只有发起连接终止的一方会进入 TIME_WAIT 状态,现在我们来思考一下 TIME_WAIT 的作用。
为什么不直接进入 CLOSED 状态,而要停留在 TIME_WAIT 这个状态?

  1. 这样做是为了确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
    举个例子:以防一些情况下TCP 报文传输会出错,需要重传,如果ACK 传输失败,那么FIN 报文会被对端再次发出,若没有维护 TIME_WAIT 状态,直接进入CLOSED 状态,就失去了当前状态的上下文,只能回复RST 报文,从而导致被动关闭方出现错误。

  2. 防止延时的报文‘迟到’, 此时旧连接已经不存在,但是恰巧有四元组相同的新连接,这个时候报文会被误发,对TCP 通信产生影响。所以,按照TCP 设计的规范,经过2MSL 的时间,旧报文就会被丢弃,从而解决上述这个问题。

2MSL 的时间是从主机 1 接收到 FIN 后发送 ACK 开始计时的;如果在 TIME_WAIT 时间内,因为主机 1 的 ACK 没有传输到主机 2,主机 1 又接收到了主机 2 重发的 FIN 报文,那么 2MSL 时间将重新计时。道理很简单,因为 2MSL 的时间,目的是为了让旧连接的所有报文都能自然消亡,现在主机 1 重新发送了 ACK 报文,自然需要重新计时,以便防止这个 ACK 报文对新可能的连接化身造成干扰。

TIME_WAIT 的坏处

  1. 内存资源占用, 这个问题几乎可以忽略

  2. 对端口资源的占用, 一个 TCP 连接至少消耗一个本地端口。要知道,端口资源也是有限的,一般可以开启的端口为 32768~61000 ,也可以通过net.ipv4.ip_local_port_range 指定,如果 TIME_WAIT 状态过多,会导致无法创建新连接。

如何优化 TIME_WAIT ?

  1. net.ipv4.tcp_max_tw_buckets 默认为18000 , 这个值的含义是,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有 TIME_WAIT 连接状态重置,并打印出警告信息。不过这个方法过于暴力,治标不治本,带来的问题远比解决的问题多,不推荐使用。
  1. 调低 TCP_TIMEWAIT_LEN,重新编译系统,需要一些内核方面的知识,操作起来有一定的困难。

  2. 设置 SO_LINGER ,比较危险,暂不详细阐述

  3. net.ipv4.tcp_tw_reuse
    那么 Linux 有没有提供更安全的选择呢?
    就是net.ipv4.tcp_tw_reuse选项。
    Linux 系统对于net.ipv4.tcp_tw_reuse的解释如下:

Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0.It should not be changed without advice/request of technical experts.

这段话的大意是从协议角度理解如果是安全可控的,可以复用处于 TIME_WAIT 的套接字为新的连接所用。
那么什么是协议角度理解的安全可控呢?
主要有两点:

  1. 只适用于连接发起方(C/S 模型中的客户端);
  2. 对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用。

使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即net.ipv4.tcp_timestamps=1(默认即为 1)。

要知道,TCP 协议也在与时俱进,RFC 1323 中实现了 TCP 拓展规范,以便保证 TCP 的高可用,并引入了新的 TCP 选项,两个 4 字节的时间戳字段,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。由于引入了时间戳,我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。

总结

  • TIME_WAIT 的引入是为了让 TCP 报文得以自然消失,同时为了让被动关闭方能够正常关闭;
  • 不要试图使用SO_LINGER设置套接字选项,跳过 TIME_WAIT;
  • 现代 Linux 系统引入了更安全可控的方案,可以帮助我们尽可能地复用 TIME_WAIT 状态的连接。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容