网络是怎样连接的(八)—— 探索套接字

319 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

往期文章

前言

在上一篇文章中,我们已经了解了协议栈的内部结构,接下来我们探索下在数据收发中扮演关键角色的套接字;

什么是套接字

套接字是一块内存空间,存放控制信息的内存就是套接字实体。

在协议栈内部,有一块用于存放控制信息的内吞空间,这里记录了用于控制通信操作的控制信息,例如通信对象的 IP 地址、端口号、通信操作的进行状态等。

本来套接字就只是一个概念而已,并不存在实体,如果一定要赋予它一个实体,我们可以说这些控制信息就是套接字的实体,或者说存放控制信息的内存空间就是套接字的实体。

协议栈在控制操作时需要参阅这些控制信息。  例如,在发送数据时,需要看一看套接字中的通信对象的 IP 地址和端口号,以便向指定的 IP 地址和端口发送数据。在发送数据之后,协议栈需要等待对方返回收到数据的响应信息,但数据也可能在中途丢失,永远也等不到对方的响应。在这样的情况下,我们不能一直等待下去,需要在的等待一定时间之后重新发送丢失的数据,这就需要协议栈能够知道执行发送数据操作后过了多长时间。为此,套接字中必须要记录是否已经收到响应,以及发送数据后经过了多长时间,才能根据这些信息按照需要执行重发操作。

套接字中记录了用于控制通信操作的各种控制信息,协议栈则需要根据这些信息判断下一步的行动,这就是套接字的作用。

协议栈是根据套接字中记录的控制信息来工作的。

套接字信息查看

既然套接字中记录了用于控制通信操作的各种信息,那么我们应该如何查看这些控制信息呢。其实通过 netstat 命令就可以,下面是 netstat 命令的返回结果,其中各个字段的含义如下:

  • Proto:协议类型,使用 TCP/IP 协议通信的情况下,会显示 TCP 或 UDP;
  • Recv-Q:接收队列,Q 是 Queue 的缩写;这两个值通常应该是 0,如果不是 0 可能是有问题的。packets 在队列中不应该有堆积状态,可接受短暂的非 0 情况;
  • Send-Q:发送队列,同上;
  • Local Address:运行 netstat 命令的计算机本身(本地端)的 IP 地址和端口号。0.0.0.0.0 表示不绑定 IP 地址;
  • Foreign Address:通信对象(远程端)的 IP 地址和端口号。0.0.0.0 表示还没有开始通信,没有绑定 IP 地址和端口号。此外,UDP 协议中的套接字不绑定对方的地址和端口号,因此显示 *:*
  • State:表示通信状态,如 SYN_SENTESTABLISHED 等,这些状态在 TCP 三次握手和四次挥手中会经常遇到;
Active Internet connections
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0      0  localhost.52476        android.clients..https SYN_SENT
tcp4       0      0  localhost.52475        android.clients..https SYN_SENT
tcp4       0      0  localhost.52474        optimizationguid.https SYN_SENT
tcp4       0      0  localhost.52473        optimizationguid.https SYN_SENT
tcp4       0      0  localhost.52472        42.180.124.41.https    ESTABLISHED
tcp4       0      0  localhost.52471        img7.iqiyipic.co.https ESTABLISHED
tcp4       0      0  localhost.52469        msg.qy.net.http        CLOSE_WAIT
tcp4       0      0  localhost.52468        msg.qy.net.http        CLOSE_WAIT
tcp4       0      0  localhost.52467        120.52.51.8.http       CLOSE_WAIT
tcp4       0      0  localhost.52466        123.125.10.106.http    CLOSE_WAIT
tcp4       0      0  localhost.52460        scs-hcdn.iqiyi.c.http  CLOSE_WAIT
tcp4       0      0  localhost.52459        scs-hcdn.iqiyi.c.http  CLOSE_WAIT
tcp4       0      0  localhost.52452        17.57.145.25.5223      ESTABLISHED
tcp4       0      0  localhost.52436        msg.video.dns.iq.http  ESTABLISHED
tcp4       0      0  localhost.52429        114.250.64.38.https    ESTABLISHED
tcp4       0    517  localhost.52418        tsa03s06-in-f10..https FIN_WAIT_1
tcp4       0      0  localhost.52413        msg.video.dns.iq.http  ESTABLISHED
tcp4      63      0  localhost.52238        112.240.57.228.https   CLOSE_WAIT
tcp4      24      0  localhost.52237        media-router-app.https CLOSE_WAIT
tcp4       0      0  localhost.52236        112.240.57.228.https   CLOSE_WAIT
tcp6       0      0  fe80::aede:48ff:.50885 fe80::aede:48ff:.49223 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.50846 fe80::aede:48ff:.49212 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49597 fe80::aede:48ff:.49233 ESTABLISHED
tcp4       0      0  localhost.49580        wss.app.yinxiang.https ESTABLISHED
tcp4       0      0  localhost.49554        analytics.yinxia.https ESTABLISHED
tcp4       0      0  localhost.49497        tm-in-f188.1e100.5228  ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49190 fe80::aede:48ff:.49222 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49185 fe80::aede:48ff:.49217 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49184 fe80::aede:48ff:.49210 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49161 fe80::aede:48ff:.49215 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49157 fe80::aede:48ff:.49234 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49160 fe80::aede:48ff:.49214 ESTABLISHED
tcp6       0      0  fe80::aede:48ff:.49153 fe80::aede:48ff:.59602 ESTABLISHED
udp4       0      0  *.54230                *.*
udp4       0      0  *.64738                *.*
udp4       0      0  *.56827                *.*

创建套接字

看了套接字的具体样子之后,我们再来看看协议栈是如何创建套接字的。

首先,应用程序调用 Socket 库申请创建套接字,协议栈根据应用程序的申请执行创建套接字的操作。

在这个过程中,协议栈首先会分配用于存放一个套接字所需的内存空间。  用于记录套接字控制信息的内存空间并不是一开始就存在的,因此我们先要开辟出这样一块空间来,这相当于为控制信息准备一个容器。但光一个容器并没有什么用,还需要往里面存入控制信息。套接字刚刚创建时,数据收发操作还没有开始,因此需要在套接字的内存空间中写入表示这一初始状态的控制信息。到这里,创建套接字的操作就完成了。

接下来,需要将表示这个套接字的描述符告知应用程序。描述符相当于用来区分协议栈中的多个套接字的号码牌。收到描述符之后,应用程序在想协议栈进行收发数据委托时就需要提供这个描述符。由于套接字中记录了通信双方的信息以及通信处于怎样的状态,所以只要通过描述符确定了相应的套接字,西驿站就能够获取所有的相关信息,这样一来,应用程序就不需要每次都告诉协议栈应该和谁进行通信了。

总结

本篇文章中我们探索了套接字的结构,以及如何创建套接字。

套接字就只是一个概念而已,并不存在实体,如果一定要赋予它一个实体,我们可以说这些控制信息就是套接字的实体,或者说存放控制信息的内存空间就是套接字的实体。

套接字中记录了用于控制通信操作的各种控制信息,协议栈则需要根据这些信息判断下一步的行动,这就是套接字的作用。

我们通过 netstat 命令来查看套接字的详细信息。

希望能够对大家有所帮助!

参考文章

  • 《网络是怎样连接的》—— 户根勤