无名套接口 套接口并不总是需要有一个地址。例 如,socketpair函数创建了两个彼此相连的两个套接口,但是却没有地址。实际上,他们是无名套接口。想像一下冷战期间美国总统与苏联之间的红色电 话。他们任何一端并不需要电话号码,因为他们是直接相连的。同样,socketpair函数也是直接相连的,也并不需要地址。 匿名调用 有时在实际上,连接中的两个套接口中的一个也没有地址。对于要连接的远程套接口,他必须要有一个地址来标识。然而,本地套接口是匿名的。建立起来的连接具有一个有地址的远程套接口和另一个无地址的套接口。 生成地址 有 时我们并不会介意我们的本地址是什么,但是我们需要一个来进行通信。这对于需要连接到一个服务器(例如一个RDBMS数据服务)的程序来说通常是正确的。 他们的本地地址仅为持续的连接所需要。分配确定的地址也可以完成,但是这增加了网络管理的工作。相应的,当地址可用时才会生成地址。 理解域 当Berkeley开发组正在构思BSD套接口接口时,TCP/IP仍在开发之中。与此同时,有一些其他的即将完成的协议正在为不同的组织所使用,例如X.25协议。其他的协议也正在研究之中。 我 们在上一章所见的socketpair函数,以及我们将会看到的socket函数,很明智的允许了其他协议需不是TCP/IP也许会用到的可能性。 socketpair函数的domain参数允许这种约束。为了讨论的方便,让我们先来回顾一下socketpair函数的概要: #include <sys/types.h> #include <sys/socket.h> int socketpair(int domain, int type, int protocol, int sv[2]); 通常,protocol参数指定为0。0允许操作系统选择我们所选择的domain的所用的默认协议。对于这些规则有一些例外,但是这超出了我们讨论的范围。 现在我们要来解释一下domain参数。对于socketpair函数,这个值必须为AF_LOCAL或者AF_UNIX。在上一章,我们已经指出AF_UNIX宏与旧版的AF_LOCAL等同。然而AF_LOCAL意味着什么?他选择了什么呢? 常量的AF_前缘指明了地址族。domain参数选择要使用的地址族。 格式化套接口地址 每 一个通信协议指明了他自己的网络地址的格式。相应的,地址族用来指明要使用哪种类型的地址。常量AF_LOCAL(AF_UNIX)指明了地址将会按照本 地(UNIX)地址规则来格式化。常量AF_INET指明了地址将会符合IP地址规则。在一个地址族中,可以有多种类型。 在下面的部分中,我们将会检测各种地址族的格式以及物理布局。这是需要掌握的重要的一部分。人们使用BSD套接口接口时所遇到的困难,很多与地址初始化相关。 检测通常的套接口地址 因为BSD套接口地址的开发早于ANSI C标准,所以没有(void *)数据指针来接受任何结构地址。相应的BSD的解决选择是定义一个通用的地址结构。通用的地址结构是用下面的C语言语句来定义的: #include <sys/socket.h> struct sockaddr { sa_family_t sa_family; /* Address Family */ char sa_data[14]; /* Address data. */ }; 这里的sa_family_t数据类型是一个无符号短整数,在Linux下为两个字节。整个结构为16个字节。结构元素的sa_data[14]代表了地址信息的其余14个字节。 下图显示了通用地址结构的物理布局: 通用套接口地址结构对于程序而言并不是那样有用。然而,他确实提供了其他地址结构必须适合的引用模型。例如,我们将会了解到所有地址必须在结构中的同样的位置定义一个sa_family成员,因为这个元素决定了地址结构的剩余字节数。 格式化本地地址 这个地址结构用在我们的本地套接口中(我们的运行Linux的PC)。例如,当我们使用lpr命令排除要打印的文件时,他使用一个本地套接口与我们的PC上假脱机服务器进行通信。虽然也可以用TCP/IP协议来进行本地通信,但是事实证明这是低效的。 传统上,本地地址族已经被称这为AF_UNIX域。这是因为这些地址使用本地UNIX文件来作为套接口名字。 AF_LOCAL或者AF_UNIX的地址结构名为sockaddr_un。这个结构是通过在我们的C程序中包含下面的语句来定义的: #include <sys/un.h> sockaddr_un的地址结构: struct sockaddr_un { sa_family_t sun_family;/* Address Family */ char sun_path[108]; /* Pathname */ }; 结构成员sun_family的值必须为AF_LOCAL或者AF_UNIX。这个值表明这个结构是通过sockaddr_un结构规则来进行格式化的。 结构成员sun_path[108]包含一个可用的UNIX路径名。这个字符数组并不需要结尾的null字节。 在下面的部分中,我们将会了解到如何来初始化一个AF_LOCAL地址与定义他的长度。 格式化传统本地地址 传统本地地址的地址名空间为文件系统路径名。一个进程也许会用任何可用的路径名来命名他的本地套接口。然则为了可用,命名套接口的进程必须可以访问路径名的所有目录组件,并且有权限来在指定的目录中创建最终的套接口对象。 一些程序员喜欢在填充地址结构之前将其全部初始化为0。这通常是通过memset函数来做到的,并且这是一个不错的主意。 struct sockaddr_un uaddr; memset(&uaddr,0,sizeof uaddr); 这个函数会为我们将这个地址结构的所有字节设置为0。 下面的例子演示了一个简单的初始化sockaddr_un结构的C程序,然后调用netstat命令来证明他起到了作用。在这里我们要先记住在socket与bind上的程序调用,这是两个我们还没有涉及到的函数。 /***************************************** * * af_unix.c * * AF_UNIX Socket Example: * * ******************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> /* * This function reports the error and * exits back to the shell: */ static void bail(const char *on_what) { perror(on_what); exit(1); } int main(int argc,char **argv,char **envp) { int z; /* Status return code */ int sck_unix; /* Socket */ struct sockaddr_un adr_unix; /* AF_UNIX */ int len_unix; /* length */ const char pth_unix[] = "/tmp/my_sock"; /* pathname */ /* * Create a AF_UNIX (aka AF_LOCAL) socket: */ sck_unix = socket(AF_UNIX,SOCK_STREAM,0); if(sck_unix == -1) bail("socket()"); /* * Here we remove the pathname for the * socket,in case it existed from a * prior run.Ignore errors (it maight * not exist). */ unlink(pth_unix); /* * Form an AF_UNIX Address: */ memset(&adr_unix,0,sizeof adr_unix); adr_unix.sun_family = AF_LOCAL; strncpy(adr_unix.sun_path,pth_unix, sizeof adr_unix.sun_path-1) [sizeof adr_unix.sun_path-1] = 0; len_unix = SUN_LEN(&adr_unix); /* * Now bind the address to the socket: */ z = bind(sck_unix, (struct sockaddr *)&adr_unix, len_unix); if(z == -1) bail("bind()"); /* * Display all of our bound sockets */ system("netstat -pa --unix 2>/dev/null |" "sed -n '/^Active UNIX/,/^Proto/P;" "/af_unix/P'"); /* * Close and unlink our socket path: */ close(sck_unix); unlink(pth_unix); return 0; } 上面的这个例子的步骤如下: 1 在第28行定义了sck_unix来存放创建的套接口文件描述符。 2 在第29行定义了本地地址结构并且命名为adr_unix。这个 |
|