1. 引言
网络通信的本质仍然是进程间通信。
几个月前,我们在学习进程间通信的时候(共享内存、管道、以及用于同步的信号量),并没有提到过网络 IPC,那时候是因为基础不太够。另一方面,之前讲的进程间通信,所有的进程都位于同一个操作系统上。
然而网络通信跨越了这条“鸿沟”,穿越了世俗的界限,将进程间通信扩展到了不同的计算机上。
在同一个操作系统上,不同进程之间可以使用进程的 pid 号进行唯一标识。可是在不同计算机上,这种方法就行不通了,因为不同计算机上,也会有两个一样的进程 id 号。
所以在网络 ipc 中,为了标识目标的通信进程,只能使用 ip 地址,光有 ip 地址也是不够的啊!!!有人想到 ip 地址 + 进程 pid 两者联合起来,好吧,,,是你脑洞大开还是我脑洞大开……这种方法确实可行,但是它用起来并不是那么爽,因为每次你要和另一台电脑上的程序通信,你都得知道它的 pid,这一次它的 pid 是 2000,说不定一次重新启动后,就变成了 2017.
2. IP 地址 + 端口号(port)
实际上,标识目标进程的方案是使用 IP 地址 + 端口号的方式,我知道你可能又懵圈了,端口号是一个新概念。实际上它就是一个 unsigned short 类型(2字节)的整数,所以说,最大的端口号是 65535.
首先,端口号首先是稳定的,进程每次启动,都可以使用固定的端口号。端口号可以唯一标识当前操作系统的一个进程,或者说,一个端口号只能被一个进程使用。
如果要进行网络通信的话,就可以使用 (IP 地址 + 端口号)来唯一的标识这个世界上的任何一个进程。(你得要有一定的 IP 地址的基础才能理解这句话,有些 IP 地址是私有地址,不能出现在公网上,这种 IP 地址就是我们常用的局域网 IP 地址,比如 192.168.XXX.XXX 这些。如果你是局域网内的计算机,你的计算机是无法被外网所访问到的,可能你会狡辩说你的计算机能上网,这个往深了说就是 NAT 技术……哎,跑远了。)
3. 套接字地址
在网络编程中,把(IP 地址 + 端口号)这样的一对值称呼为套接字地址,又懵圈了吧,,,新名词套接字又是啥?
套接字的英文原文 socket,看看 Collins 的解释:
不得不说,中文真的是博大精深,将 socket 翻译成套接字的那个译者没获得诺贝尔文学奖还真是可惜了。
所以这里的套接字,实质上应该是类似一种插槽的东西,不如就说是插座吧。那么,套接字地址就是“插座”地址了。再者,是谁的“插座”?没错,是这个进程上的插座。
在计算机中,通常使用 192.168.166.5:80
这种以 ip:port 的形式来表示套接字地址。
图1 某个进程上绑定了很多插座
图 1 中的进程,就安装了 3 个插座,什么意思呢?就是说这个进程它使用了 3 个端口号,我们使用这三个套接字地址中的任何一个都可以联系这个进程。
所以,有了套接字地址了,和进程通信也不是什么难事了。不过,值得一提的是,这些插座,是本来就有的,还是需要自己去安装的?这个问题在下一讲讨论。
4. 套接字地址在程序中的表示
刚刚用 ip:port 的方式来表示套接字地址,实际上这只是给人类看的,容易看的明白,在程序中,显然就是结构体了。就算你猜也猜的到这个结构体大概长什么样子了,是吧?
这个结构体的样子如下:
struct sockaddr_in {
sa_family_t sin_family; /* 这个值固定写 AF_INET */
in_port_t sin_port; /* 网络字节序的端口号 */
struct in_addr sin_addr; /* in_addr 类型的 ip 地址 */
};
后面两个字段不用我解释了,第一个字段现在我也不解释,以后再说……
5. 总结
- 网络通信使用套接字地址唯一的标识进程
- 知道套接字地址是什么东东