# 【NO.200】[底层原理]Socket 究竟是什么? 为啥网络离不开 Socket？

## 1.**前言**

一说到网络，大家必然会想到 TCP、UDP、Http、三握四挥等，但是一说 Socket，大家可能会有点模糊了，只知道网络中会用到，但是 Socket 究竟是什么? 套接字又是啥？为啥网络离不开 Socket？

## 2.**Socket 是什么?**

Socket 其实就是套接字，大部分人对于 Socket 的理解就是它可以实现一个简单的网络通信，但是它**「具体解决了哪些问题？有什么实际的作用？为什么会有一个 Socket 出现？」**

![img](https://pic4.zhimg.com/80/v2-4368018a29a51156bf2f708782a9460f_720w.webp)

Socket 其实是在**「应用层与传输层之间的一个产物」**，它把传输层的很多复杂操作封装成一些简单的接口，来让应用层调用以此来实现进程在网络中的通信，Socket 是对端口通信开发的工具,它要更底层一些。

> Socket 其实类似于一台洗碗机，它的功能就是洗碗(网络通信)，如果没有它，你可能需要自己手动去洗碗(手动调用传输层、应用层之间的各个 api)，但是有了它你只需要点击开关、调整时长就行了(封装了 api)，你可以不需要它，但是如果没有它，洗碗(应用层与传输层之间的交互)将变得非常繁琐。

![img](https://pic3.zhimg.com/80/v2-d637527bfa14e29f80304dcda6c740b6_720w.webp)

一次完整的网络通信必不可少的会经过物理传输层的网线和网卡，网络传输层的 IP 协议可以知道要将数据传送给哪台机器，但是在计算机系统中会运行不同进程，那要如何把**「网卡中的网络数据识别出来是给哪个进程的」**，这其实就是 Socket 设计的想解决的一点了。

Socket 是**「对 TCP/IP 或者 UDP/IP 协议的封装」**，Socket 本身其实就是一个调用接口。通过这个接口我们在开发网络应用程序的时候，就可以不用关心底层是怎么实现的，减轻开发的难度。

## **3.Socket 运行流程**

### 3.1 **基于 TCP**

![img](https://pic4.zhimg.com/80/v2-7f0376657c465a412f4a43ecb47a8ec3_720w.webp)

### 3.2 **Server**

- socket():表示创建一个 socket，底层会生成一个文件描述符，用来表示该 socket
- bind():用来绑定服务的端口，地址，这里一般都是以固定的为主，因为在客户端连接的时候需要指定
- listen():当绑定完成之后，listen 就会监听这个端口的数据包
- accept():相当于一个开关，表示我准备好了，可以接受请求了，但是这里会一直阻塞，直到客户端连接成功
- read():读取客户端发送过来的内容
- write():客户端写入要返回的数据
- close():断开连接，**「四次挥手」**

### 3.3 **Client**

- socket():表示创建一个 socket，底层会生成一个文件描述符，用来表示该 socket
- connet():表示与指定地址进行连接，在此之前，会随机创建自己的端口，tcp 的**「三次握手就是从这里开始」**的
- write():客户端写入要发送的数据
- read():客户端读取服务端返回的数据
- close():断开连接，**「四次挥手」**，给客户端发送断开连接的信息

### 3.4 **基于 UDP**

![img](https://pic4.zhimg.com/80/v2-b84ab0043b969ee98aebefb4b7e1199b_720w.webp)

这里我就不细写了，其实大同小异，从流程图上就可以看到

因为 UDP 是无状态的，所以对于服务端来说没有连接，并且其会在调用 Recvfrom() 方法后就收客户端的请求，并一直阻塞，直到收到信息

## 4.**Socket TCP 是如何建立连接的**

在 Socket 绑定完服务器的地址后，就开始和服务器建立连接了，TCP 建立连接的方式其实就是大名鼎鼎三次握手了

![img](https://pic4.zhimg.com/80/v2-5f3599a8b866e214d9bbf01d9257c713_720w.webp)

- 第一次握手:A 的 TCP 进程创建一个 传输控制块 TCB ，然后向 B 发出连接请求报文段。之后将同步位 SYN 设置为 1，同时选择一个初始序列号 seq=x，这时客户端 A 进入到 SYN-SENT（同步已发送）状态。
- 第二次握手:B 收到连接请求报文段，如果同意建立连接，则向 A 发送确认。在确认报文段中 同步位 SYN=1、确认位 ACK=1、确认号 ack=x+1，同时也为自己选择一个初始序列号 seq=y，这时服务器 B 进入 SYN-RCVID 状态。
- 第三次握手:A 收到 B 的确认以后，再向 B 发出确认。确认报文 ACK=1、确认号ack=y+1。这时A进入到 ESTAB-LISHED 状态。当B接收到A的确认后，也进入 ESTAB-LISHED 状态。连接建立完成

### 4.1**三次握手发生在 socket 的哪几个函数中**

![img](https://pic1.zhimg.com/80/v2-c58c69377a495f6f76d77728dc5b8040_720w.webp)

- 当客户端调用 connect 时，触发了连接请求，向服务器发送了SYN 信号，这时 connect 进入阻塞状态；
- 服务器监听到连接请求，即收到 SYN，调用 accept 函数接收，进入阻塞状态，在此之前会尽力 socket、bind、listen 函数；然后返回相关的 syn 以及 ack 信号
- 客户端接受到服务端的信息，此时 connect 完成，解除阻塞状态，并且向服务端发送 ack 信号
- 服务端收到 ack， accept 阻塞解除，完成连接

在建立连接之后，connect() 就已经执行完毕了，服务端就可以向客户端发送数据了。

## 5.**Socket TCP 是如何断开连接的**

![img](https://pic3.zhimg.com/80/v2-9e7c0041718ccd6d0e3d86920144794a_720w.webp)

- 第一次挥手:A 先发送连接释放报文段，段首部的终止控制位 FIN=1，序号seq=u（等于A前面发送数据的最后一个序号加1）；然后 A 进入 FIN-WAIT-1（终止等待1）状态，等待 B 的确认。

- 第二次挥手:B 收到 A 的连接释放报文段后，立刻发出确认报文段，确认号 ack=u+1，序号 seq=v（等于 B 前面发送数据的最后一个序号加1）；然后 B 进入 CLOSE-WAIT（关闭等待）状态。

- 第三次挥手:A 收到 B 的确认报文段后进入到 FIN-WAIT-2（终止等待2）状态，继续等待 B 发出连接释放报文段；

- - 若 B 已经没有数据要发送，B 就会向 A 发送连接释放报文段，段首部的终止控制位 FIN=1，序号 seq=w（半关闭状态可能又发送了一些数据），确认号 ack=u+1，这时B进入 LAST-ACK（最后确认）状态，等待A的确认。

- 第四次挥手:A收到B的连接释放报文段并发出确认，确认段中 确认位 ACK=1，确认号 ack=w+1，序号 seq=u+1；然后 A 进入到TIME-WAIT（时间等待）状态。当 B 再接收到该确认段后，B 就进入 CLOSED状态。

### 5.1**第四次挥手后为何要等待 2MSL**

首先 2MSL 的时间是从客户端(A)接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内，因为客户端(A)的 ACK 没有传输到服务端(B)，客户端(A)又接收到了服务端(B)重发的 FIN 报文，那么 2MSL 时间会被重置。等待 2MSL 原因如下

- 1.得原来连接的数据包消失

- - 如果B没有收到自己的ACK，会超时重传FiN那么A再次接到重传的FIN，会再次发送ACK
  - 如果B收到自己的ACK，也不会再发任何消息

在最后一次挥手后 A 并不知道 B 是否接到自己的信息， 包括 ACK 是以上哪两种情况，A 都需要等待，要取这**「两种情况等待时间的最大值，以应对最坏的情况发生」**，这个最坏情况是：去向ACK消息最大存活时间（MSL) + 来向FIN消息的最大存活时间(MSL)。这刚好是2MSL，这个时间，足以使得原来连接的数据包在网络中消失。

- 2.保证 ACK 能被服务端接收到从而正确关闭链接

因为这个 ACK 是有可能丢失的，会导致服务器收不到对 FIN-ACK 确认报文。假设客户端不等待 2MSL ，而是在发送完 ACK 之后直接释放关闭，一但这个 ACK 丢失的话，服务器就无法正常的进入关闭连接状态。

原文地址：https://zhuanlan.zhihu.com/p/540665324

作者：linux