(本篇文章文字较多,耐性看完,你会有不小的收成)
哈喽咱们好,我是咸鱼
我信任咱们在面试进程中或多或少都会被问到这样一个问题:你能解释一下什么是 socket 吗?
我记得我当初的答复很是粗浅:socket 也叫套接字,用来担任不同主机程序之间的网络通讯衔接,socket 的表现办法由四元组(ip地址:端口)组成
那么今天,咸鱼将跟咱们打开 socket 的奥秘大门,不但要搞清楚 socket 的概念,最好还能够了解它的底层完成
咱们首要检查一下 socket 的翻译
咱们看到,socket 能够翻译成插座、插头
那现在请幻想这么一个场景:给手机充电时,你将充电插头插入电源插座里边,是不是意味着插座与充电插头衔接起来了
在计算机世界中,socket 翻译成套接字,经过 socket 咱们能够与某台服务器进行衔接,而建立衔接的进程,你能够脑补成将充电插头插进插座的进程
socket 使用场景
假定咱们想要将数据从 A 电脑的某个进程传送到 B 电脑的某个进程(比方咸鱼用微信发信息给冰冰)
那么在与对方谈天的进程中,其实便是这两台电脑中的微信进程相互传输数据的进程
在这个进程中,两台电脑各自调用 socket 办法,然后会得到一个 fd 句柄(socket_fd),这个 fd 句柄就相当于 socket 的身份证号
得到 fd 句柄之后:
-
服务端执行 bind()、listen()、accept() 办法等待客户端建立衔接的恳求
-
客户端执行 connect() 办法向服务端发起衔接
-
衔接建立起来之后,两头都能够执行 send()、recv() 办法来相互传递数据
PS: 关于不同的传输层协议,上面这个进程是不一样的,概况能够检查我之前的文章《Python 网络编程》
TCP 协议
UDP 协议
socket 底层规划
咱们知道了 socket 是用来完成网络传输功用的,它担任不同主机进程之间的网络通讯衔接
我将上面的问题改一下,把 ”socket 是什么“ 改成 ”假如让你来完成一个网络传输功用,你会怎样规划“
网络传输功用,简单点来讲便是两头服务器之间进行网络通讯并相互收发数据,收发数据也便是读写数据
首要咱们会遇到第一个问题:茫茫互联网中你怎样能找到那台梦中情机
聪明的你肯定会想到——ip地址!咱们用 ip 地址来定位电脑
找到了你的梦中情机之后,你会发现,一台电脑上面这么多进程,我怎样才干找到与我通讯的那个进程(比方说微信)
聪明的你很快就想到了用端口号(port)
能够这么了解,ip 地址是用来定位街区的,而端口号 port 对应这个街区中的门牌号,经过 ip +port 的组合,你能够在茫茫互联网中找到属于你的梦中情机并且与之通讯
所以你在规划网络传输功用初期,界说了一个数据结构 sock,sock 里边包含了 ip 和 port 字段 (假定用 C 言语完成)
在 Linux 中(以 CentOS 7举例),在头文件/usr/include/netinet/in.h
能够看到担任套接字地址的 sock 结构体
sin_family字段为AF_INET,sin_port表明端口号,sin_addr表明IPv4地址,是一个
struct in_addr
类型的结构体sin6_family 字段为 AF_INET6,sin6_port 表明端口号,sin6_addr 表明 IPv6 地址,是一个 struct in6_addr 类型的结构体
处理了定位问题之后,咱们知道在计算机网络中有许多协议,这些协议规定了计算机之间的通讯办法
比方你是选用可靠的 TCP 协议去进行网络通讯,仍是相对不可靠的 UDP 协议
不同的网络协议还对应着不同的网络通讯场景,假如你挑选了 TCP协议,你还得考虑例如滑动窗口、超时重传这些场景
所以有了 ip 和 port 还不可,你还需求界说新的数据结构用来保护网络协议以及对应的网络场景
又因为不同的网络协议中有一些功用类似的办法(例如收发数据),所以你决议将不同协议中公共的部分提取出来,经过”承继“的办法来完成功用复用
所以能够先界说一个名为 sock 的数据结构,然后界说”承继“ sock 的各类 sock
PS:Linux 内核是用 C 言语完成的,在 C 言语中没有承继这个概念,你能够简单将这个承继了解成 xx_sock 根据 sock 进行了扩展,xx_sock 是 sock 的进阶版
-
sock
:最基础的结构,用来保护任何网络协议都会用到的收发数据缓冲区(共用部分) -
inet_sock
:担任网络传输功用的 sock,在 sock 基础上加了 TTL(网络生存时间)、ip 和 port 这些跟网络传输相关的字段信息 -
inet_connection_sock
:面向衔接的 sock,在inet_sock
基础上添加了面向衔接的协议里相关字段,比方 accept 队列,数据包分片巨细,握手失利,重试次数等;尽管咱们现在提到面向衔接的协议便是指 TCP,但从规划上 Linux 需求支撑扩展其他面向衔接的新协议,比方 SCTP 协议,所以说tcp_sock
则是在这个基础上完成的真正的 TCP 协议专用 sock 结构
上面比如中的这些 sock 都能够在体系上直接找到,以 CentOS 7 为例
现在你用代码完成了这一堆数据结构——sock,不同的 sock 分别完成自己责任内的功用(担任面向衔接的数据结构 inet_connection_sock
、担任 UDP 协议的数据结构 udp_sock
等等)
但是你需求这些 sock 去跟硬件网卡交互才干完成网络传输的功用,已然需求跟硬件交互,那就说明需求比较高的操作体系权限
同时考虑到性能和安全,这套数据结构不能放在用户态,需求给它放到 体系内核里边
已然这套数据结构在内核里,处在用户态的程序想要用这套数据结构来完成网络传输功用该怎样办呢?
除此之外,处在用户态的程序并不关怀也不知道你这套数据结构在底层内核是怎样操作的,功用是怎样完成的,它只关怀成果
所以你想到了用接口调用的办法——你将一个个功用笼统一个个接口,今后他人只需求调用这些接口,就能够让内核中这一大堆杂乱的数据结构去完成指定功用
又因为在 Linux 中一切皆文件,你索性将这些 sock 封装成文件,当用户态的程序去调用你供给的接口时,需求先创建一个 sock 文件
这个新生成的 sock 文件有一个文件句柄 fd,用户态的程序只需求拿着这个 fd 就能够对内核中的 sock 进行操作
上面有提到,你将不同的数据结构(inet_sock
、tcp_sock
等等)笼统成一个个 API 接口,今后他人只需求调用这些 API 接口就能够驱动咱们写好的这一大堆杂乱的数据结构去进行网络传输
下面列出了一些常见的接口:
-
send
-
recv
-
bind
-
listen
-
connect
到这里,整个网络传输功用就现已基本完成了。上面罗列出来的这些办法,其实便是 socket 供给出来的接口
到这里,咱们对 socket 有了一个更深地了解——socket 其实相当于一个接口层,它处在内核态和用户态之间:
- 向上用户态
-
- 为处在用户态的程序供给 API 接口,方便用户态程序完成网络传输功用
- 向下内核态
-
-
对网卡进行操作,担任网络传输作业
-
或者你也能够这么了解,处在用户态的程序经过 socket 供给的接口,将网络传输的这部分作业外包给了 Linux 内核
咱们以 tcp 协议为比如来看下 python 中是如何操作 socket 的
在客户端中,程序首要调用 socket 供给的 socket 办法创建一个 socket 文件来获得 socket 句柄,然后调用 connect 办法,这时候内核会依据 socket_fd 找到对应的 sock 文件
再依据文件里的信息找到处在内核的 sock 结构,经过 sock 结构与服务端进行三次握手建立衔接
衔接建立好之后,客户端调用 send 办法来进行数据传输,sock 中界说了一个发送缓冲区和接纳缓冲区,其实便是一个链表,链表上面放着一个个等待发送或接纳的数据
总结
咱们再次回到那个问题——socket 是什么?
sock(或 socket)是操作体系内核供给的一种数据结构,用于完成网络传输功用
根据不同的网络协议以及应用场景,衍生了各种类型的 sock
每个网络层协议都有相应的 sock 结构体来管理该层协议的衔接状况和数据传输。各类 sock 操作硬件网卡,就完成了网络传输的功用
为了将这些功用让处在用户态的应用程序使用,不但引入了 socket 层,还将各类功用的完成办法笼统成了 API 接口,供应用程序调用
同时将 sock 封装成文件,应用程序就能够在用户层经过文件句柄(socket fd)来操作内核中 sock 的网络传输功用
这个 socket fd 是一个 int 类型的数字,而 socket 中文翻译叫做套接字,结合这个 socket fd,你是不是能够将其了解成:一套用于衔接的数字
而 socket 分 Internet socket 和 UNIX Domain socket,两者都能够用于不同主机进程间的通讯和本机进程间的通讯
只是前者选用的是根据 IP 协议的网络通讯办法,而后者选用的是根据本地文件体系的通讯办法
关于 UNIX Domain socket,能够经过 netstat -x
检查
感谢阅读, 喜爱作者就动动小手 [ 一键三连] ,这是我写作最大的动力