⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 15.htm

📁 UNIX环境下C编程的详细详细介绍
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<p>return(-3); </p>

<p>return(fd); </p>

<p>} </p>

<p>程序15.22 用于4.3+BSD的serv_listen函数 </p>

<p>首先,调用socket函数创建一个UNIX域套接口。然后,填充sockeraddr_un结 
</p>

<p>构,将一个众所周知的路径名赋与该套接口。该结构是调用bind函数的一个参数。 
</p>

<p>然后调用listen以通知核心:本服务器正等待来自客户的连接。(listen的第二个 
</p>

<p>参数是5,它是最大的未决连接请求数,核心将这些请求对该描述符进行排队。大 
</p>

<p>多数实现强制该值的上限为5。) </p>

<p>客户调用cli_conn函数(程序15.23)起动与服务器的连接。 </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;sys/stat.h&gt; </p>

<p>#include &lt;fcntl.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>/* Create a client endpoint and connect to a server. */ </p>

<p>/* Create a client endpoint and connect to a server. */ </p>

<p>int /* returns fd if all OK, &lt;0 on error */ </p>

<p>cli_conn(const char *name) </p>

<p>{ </p>

<p>int fd; </p>

<p>/* open the mounted stream */ </p>

<p>if ( (fd = open(name, O_RDWR)) &lt; 0) </p>

<p>return(-1); </p>

<p>if (isastream(fd) == 0) </p>

<p>return(-2); </p>

<p>return(fd); </p>

<p>} </p>

<p>程序15.23 用于4.3+BSD的cli_conn函数 </p>

<p>我们调用socket函数以创建客户端的UNIX域套接口,然后客户专用的名字填入soc 
</p>

<p>ketaddr_un结构。该路径名的最后5个字符是客户的进程ID。(我们可以查证此结 
</p>

<p>构的长度是14个字符,以避免UNIX域套接口早期实现的某些错误。)在路径名已经 
</p>

<p>存在的情况下unlink,然后再调用bind将一名字赋与客户的套接口,这就创建了在 
</p>

<p>文件系统中的路径名,该文件的类型是套接口。接着调用chmod,它关闭除user_r 
</p>

<p>ead,user_write和user_execute以外的存取权。在serv_accept中,服务器检查该 
</p>

<p>套接口的这些许可权和用户ID,以验证用户的身份。 </p>

<p>然后,我们应当以服务器众所周知的路径名填充另一个socketaddr_un结构。 
</p>

<p>最后,connect函数起动与服务器的连接。 </p>

<p>创建每个客户与服务器的唯一连接是在serv_accept函数中调用accept函数实现的 
</p>

<p>(程序15.24)。 </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;sys/stat.h&gt; </p>

<p>#include &lt;stropts.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>/* Wait for a client connection to arrive, and accept it. </p>

<p>* We also obtain the client's user ID. */ </p>

<p>int /* returns new fd if all OK, -1 on error */ </p>

<p>serv_accept(int listenfd, uid_t *uidptr) </p>

<p>{ </p>

<p>struct strrecvfd recvfd; </p>

<p>if (ioctl(listenfd, I_RECVFD, &amp;recvfd) &lt; 0) </p>

<p>return(-1); /* could be EINTR if signal caught */ </p>

<p>if (uidptr != NULL) </p>

<p>*uidptr = recvfd.uid; /* effective uid of caller */ </p>

<p>return(recvfd.fd); /* return the new descriptor */ </p>

<p>} </p>

<p>程序15.24 用于4.3+BSD的serv_accept函数 </p>

<p>服务器在调用accept中堵塞以等待一客户调用cli_conn。当accept返回时, 
</p>

<p>其返回值是连向客户的全新的描述符。(这类似于SVR4中,connld模块所做的。) 
</p>

<p>另外,accept也通过其第二个参数(指向socketaddr_un结构的指针)返回客户赋 
</p>

<p>与其套接口的路径名(它包含客户的进程ID)。用NULL字节结束此路径名,然后调 
</p>

<p>用stat。这使我们可以验证此路径名确实是一个套接口,其许可权user_read,us 
</p>

<p>er_write和user_execute。我们也验证与该套接口相关的三个时间不超过30秒。( 
</p>

<p>time函数返回自UNIX纪元经过的时间和日期,它们都以秒计。)如若所有这些检查 
</p>

<p>都通过我们就认为该客户的身份(其有效用户ID)是该套接口的属主。虽然这种检 
</p>

<p>查并不完善,但却是现有系统所能做得最好的。(如果核心能象SVR4 
I_RECVFD做 </p>

<p>的那样,将有效用户ID返回给accept,那就更好一些。) </p>

<p>图15.7显示了cli_conn调用返回后的这种连接,我们假定服务器的众所周知名字是 
</p>

<p>/tmp/servi。请将此与图15.6相比较。 </p>

<p>图15.7 在UNIX域套接口上客户-服务器连接 </p>

<p>15.6 OPEN服务器,版本2 </p>

<p>在15.4节,客户调用fork和exec构造了一个Open服务器,它说明了如何从子程 
</p>

<p>序向父程序传送文件描述符。在本节开发一个精灵进程样式的OPEN服务器。一个服 
</p>

<p>务器处理所有客户的请求。我们期望,由于避免使用了fork和exec,所以这一设计 
</p>

<p>会是更有效的。在客户和服务器之间仍将使用上一节说明的三个函数:serv_list 
</p>

<p>en、serv_accept和cli_conn。这一服务器将表明:一个服务器可以处理多个客户 
</p>

<p>,为此使用的技术是12.5节中说明的select和poll函数。 </p>

<p>本节所述的客户类似于15.4节中的客户。确实,文件main.c是完全相同的(程 
</p>

<p>序15.12)。在open.h头文件(程序15.11)中则加了下面1行: </p>

<p>#define CS_OPEN &quot;/home/stevens/open&quot; /*服务器的众所周知名 </p>

<p>字*/ </p>

<p>因为在这里调用的是cli_conn而非fork和exec,所以文件open.c与程序15.13完全 
</p>

<p>不同。这示于程序15.25。 </p>

<p>#include &quot;open.h&quot; </p>

<p>#include &lt;sys/uio.h&gt; /* struct iovec */ </p>

<p>/* Open the file by sending the &quot;name&quot; and &quot;oflag&quot; to the </p>

<p>* connection server and reading a file descriptor back. */ </p>

<p>int </p>

<p>csopen(char *name, int oflag) </p>

<p>{ </p>

<p>int len; </p>

<p>char buf[10]; </p>

<p>struct iovec iov[3]; </p>

<p>static int csfd = -1; </p>

<p>if (csfd &lt; 0) { /* open connection to conn server */ </p>

<p>if ( (csfd = cli_conn(CS_OPEN)) &lt; 0) </p>

<p>err_sys(&quot;cli_conn error&quot;); </p>

<p>} </p>

<p>sprintf(buf, &quot; %d&quot;, oflag); /* oflag to ascii */ </p>

<p>iov[0].iov_base = CL_OPEN &quot; &quot;; </p>

<p>iov[0].iov_len = strlen(CL_OPEN) + 1; </p>

<p>iov[1].iov_base = name; </p>

<p>iov[1].iov_len = strlen(name); </p>

<p>iov[2].iov_base = buf; </p>

<p>iov[2].iov_len = strlen(buf) + 1; </p>

<p>/* null at end of buf always sent */ </p>

<p>len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len; </p>

<p>if (writev(csfd, &amp;iov[0], 3) != len) </p>

<p>err_sys(&quot;writev error&quot;); </p>

<p>/* read back descriptor */ </p>

<p>/* returned errors handled by write() */ </p>

<p>return( recv_fd(csfd, write) ); </p>

<p>} </p>

<p>程序15.25 csopen函数 </p>

<p>从客户到服务器之间使用的协议仍然相同。 </p>

<p>让我们先查看服务器。头文件open.h(程序15.26)包括了标准头文件,并且 
</p>

<p>说明了全局变量和函数原型。 </p>

<p>因为此服务器处理所有客户,所以它必须保存每个客户连接的状态。这是用定义在 
</p>

<p>opend.h头文件中的client数组实现的。程序15.27定义了三个处理此数组的函数。 
</p>

<p> </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;errno.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>#define CS_OPEN &quot;/home/stevens/opend&quot; /* well-known name */ </p>

<p>#define CL_OPEN &quot;open&quot; /* client's request for server */ </p>

<p>/* declare global variables */ </p>

<p>extern int debug; /* nonzero if interactive (not daemon) */ </p>

<p>extern char errmsg[]; /* error message string to return to client */ </p>

<p>extern int oflag; /* open flag: O_xxx ... */ </p>

<p>extern char *pathname; /* of file to open for client */ </p>

<p>typedef struct { /* one Client struct per connected client */ </p>

<p>int fd; /* fd, or -1 if available */ </p>

<p>uid_t uid; </p>

<p>} Client; </p>

<p>extern Client *client; /* ptr to malloc'ed array */ </p>

<p>extern int client_size; /* # entries in client[] array */ </p>

<p>/* (both manipulated by client_XXX() functions) */ </p>

<p>/* function prototypes */ </p>

<p>int cli_args(int, char **); </p>

<p>int client_add(int, uid_t); </p>

<p>void client_del(int); </p>

<p>void loop(void); </p>

<p>void request(char *, int, int, uid_t); </p>

<p>程序15.26 open.h头文件 </p>

<p>#include &quot;opend.h&quot; </p>

<p>#define NALLOC 10 /* #Client structs to alloc/realloc for */ </p>

<p>static void </p>

<p>client_alloc(void) /* alloc more entries in the client[] array */ </p>

<p>{ </p>

<p>int i; </p>

<p>if (client == NULL) </p>

<p>client = malloc(NALLOC * sizeof(Client)); </p>

<p>else </p>

<p>client = realloc(client, (client_size + NALLOC) * sizeof(Client)); </p>

<p>if (client == NULL) </p>

<p>err_sys(&quot;can't alloc for client array&quot;); </p>

<p>/* have to initialize the new entries */ </p>

<p>for (i = client_size; i &lt; client_size + NALLOC; i++) </p>

<p>client[i].fd = -1; /* fd of -1 means entry available */ </p>

<p>client_size += NALLOC; </p>

<p>} </p>

<p>/* Called by loop() when connection request from a new client arrives * </p>

<p>/ </p>

<p>int </p>

<p>client_add(int fd, uid_t uid) </p>

<p>{ </p>

<p>int i; </p>

<p>if (client == NULL) /* first time we're called */ </p>

<p>client_alloc(); </p>

<p>again: </p>

<p>for (i = 0; i &lt; client_size; i++) { </p>

<p>if (client[i].fd == -1) { /* find an available entry */ </p>

<p>client[i].fd = fd; </p>

<p>client[i].uid = uid; </p>

<p>return(i); /* return index in client[] array */ </p>

<p>} </p>

<p>} </p>

<p>/* client array full, time to realloc for more */ </p>

<p>client_alloc(); </p>

<p>goto again; /* and search again (will work this time) */ </p>

<p>} </p>

<p>/* Called by loop() when we're done with a client */ </p>

<p>void </p>

<p>client_del(int fd) </p>

<p>{ </p>

<p>int i; </p>

<p>for (i = 0; i &lt; client_size; i++) { </p>

<p>if (client[i].fd == fd) { </p>

<p>client[i].fd = -1; </p>

<p>return; </p>

<p>} </p>

<p>} </p>

<p>log_quit(&quot;can't find client entry for fd %d&quot;, fd); </p>

<p>} </p>

<p>程序15.27 处理client数组的三个函数 </p>

<p>第一次调用client_add时,它调用client_alloc、client_alloc又调用mallo </p>

<p>c为该数组的10个登记项分配空间。在这十个登记项全部用完后,再调用client_a 
</p>

<p>dd,然后是realloc以分配附加空间。依靠这种动态空间分配,我们没有在编译时 
</p>

<p>限制client数组的长度。 </p>

<p>如若出错,那么因为假定服务器是精灵进程,所以这些函数调用log_函数(见附 
</p>

<p>录B)。 </p>

<p>main函数(程序15.28)定义全局变量,处理命令行选择项,然后调用loop函 
</p>

<p>数。如若以一d选择项调用服务器,则它以交互方式运行而非精灵进程。当测试些 
</p>

<p>服务器时,使用交互运行方式。 </p>

<p>#include &quot;opend.h&quot; </p>

<p>#include &lt;syslog.h&gt; </p>

<p>/* define global variables */ </p>

<p>int debug; </p>

<p>char errmsg[MAXLINE]; </p>

<p>int oflag; </p>

<p>char *pathname; </p>

<p>Client *client = NULL; </p>

<p>int client_size; </p>

<p>int </p>

<p>main(int argc, char *argv[]) </p>

<p>{ </p>

<p>int c; </p>

<p>log_open(&quot;open.serv&quot;, LOG_PID, LOG_USER); </p>

<p>opterr = 0; /* don't want getopt() writing to stderr */ </p>

<p>while ( (c = getopt(argc, argv, &quot;d&quot;)) != EOF) { </p>

<p>switch (c) { </p>

<p>case 'd': /* debug */ </p>

<p>debug = 1; </p>

<p>break; </p>

<p>case '?': </p>

<p>err_quit(&quot;unrecognized option: -%c&quot;, optopt); </p>

<p>} </p>

<p>} </p>

<p>if (debug == 0) </p>

<p>daemon_init(); </p>

<p>loop(); /* never returns */ </p>

<p>} </p>

<p>程序15.28 main函数 </p>

<p>loop函数是服务器的无限循环。我们将示出该函数的两种版本。程序15.29是使用 
</p>

<p>select的一种版本。(在4.3+BSD和SVR4之下工作),程序15.30是使用poll(用于 
</p>

<p>SVR4)的另一种版本。 </p>

<p>#include &quot;opend.h&quot; </p>

<p>#include &lt;sys/time.h&gt; </p>

<p>void </p>

<p>loop(void) </p>

<p>{ </p>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -