更好阅读体验:《理解 TCP 和 UDP》— By Gitbook
一切皆 Socket
我们已经知道网络中的进程是通过 socket 来通信的,那什么是 socket 呢?
socket 起源于 UNIX,而 UNIX/Linux 基本哲学之一就是「一切皆文件」,都可以用「open → write/read → close」模式来操作。
socket 其实就是该模式的一个实现,socket 即是一种特殊的文件,一些 socket 函数就是对其进行的操作。
使用 TCP/IP 协议的应用程序通常采用系统提供的编程接口:UNIX BSD 的套接字接口(Socket Interfaces)
以此来实现网络进程之间的通信。
就目前而言,几乎所有的应用程序都是采用 socket,所以说现在的网络时代,网络中进程通信是无处不在,一切皆 socket
套接字接口 Socket Interfaces
套接字接口是一组函数,由操作系统提供,用以创建网络应用。
大多数现代操作系统都实现了套接字接口,包括所有 Unix 变种,Windows 和 Macintosh 系统。
套接字接口的起源
套接字接口是加州大学伯克利分校的研究人员在 20 世纪 80 年代早起提出的。
伯克利的研究者使得套接字接口适用于任何底层的协议,第一个实现就是针对 TCP/IP 协议,他们把它包括在 Unix 4.2 BSD 的内核里,并且分发给许多学校和实验室。
这在因特网的历史成为了一个重大事件。
—— 《深入理解计算机系统》
从 Linux 内核的角度来看,一个套接字就是通信的一个端点。
从 Linux 程序的角度来看,套接字是一个有相应描述符的文件。
普通文件的打开操作返回一个文件描述字,而 socket() 用于创建一个 socket 描述符,唯一标识一个 socket。
这个 socket 描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些操作。
常用的函数有:
- socket()
- bind()
- listen()
- connect()
- accept()
- write()
- read()
- close()
Socket 的交互流程
图中展示了 TCP 协议的 socket 交互流程,描述如下:
- 服务器根据地址类型、socket 类型、以及协议来创建 socket。
- 服务器为 socket 绑定 IP 地址和端口号。
- 服务器 socket 监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的 socket 并没有全部打开。
- 客户端创建 socket。
- 客户端打开 socket,根据服务器 IP 地址和端口号试图连接服务器 socket。
- 服务器 socket 接收到客户端 socket 请求,被动打开,开始接收客户端请求,知道客户端返回连接信息。这时候 socket 进入阻塞状态,阻塞是由于 accept() 方法会一直等到客户端返回连接信息后才返回,然后开始连接下一个客户端的连接请求。
- 客户端连接成功,向服务器发送连接状态信息。
- 服务器 accept() 方法返回,连接成功。
- 服务器和客户端通过网络 I/O 函数进行数据的传输。
- 客户端关闭 socket。
- 服务器关闭 socket。
这个过程中,服务器和客户端建立连接的部分,就体现了 TCP 三次握手的原理。
下面详细讲一下 socket 的各函数。
Socket 接口
socket 是系统提供的接口,而操作系统大多数都是用 C/C++ 开发的,自然函数库也是 C/C++ 代码。
socket 函数
该函数会返回一个套接字描述符(socket descriptor),但是该描述符仅是部分打开的,还不能用于读写。
如何完成打开套接字的工作,取决于我们是客户端还是服务器。