服务端
现在我们来查看一下打开的服务进程。下面就是客户进程exec的opend程序的代码。
opend.h头文件,版本1
首先我们需要有一个opend.h头文件,这个头文件包含一些标准的头文件以及声明一些全局变量和函数。
#include "apue.h"
#include <errno.h>
#define CL_OPEN "open" /* client's request for server */
extern char errmsg[]; /* error message string to return to client */
extern int oflag; /* open() flag: O_xxx ... */
extern char *pathname; /* of file to open() for client */
int cli_args(int, char **);
void request(char *, int, int);
服务进程的main函数,版本1
main函数读取s-pipe上面的客户的请求(从它的标准输入),并且调用函数request。
#include "opend.h"
char errmsg[MAXLINE];
int oflag;
char *pathname;
int main(void)
{
int nread;
char buf[MAXLINE];
for ( ; ; ) { /* read arg buffer from client, process request */
if ((nread = read(STDIN_FILENO, buf, MAXLINE)) < 0)
err_sys("read error on stream pipe");
else if (nread == 0)
break; /* client has closed the stream pipe */
request(buf, nread, STDOUT_FILENO);
}
exit(0);
}
request函数,版本1
函数request进行所有的处理工作。它调用函数buf_args来将客户的请求变成标准的参数列表形式,然后调用cli_args处理客户进程的参数。如果所有的工作做好了,那么open会被调用来打开文件,然后send_fd通过s-pipe发送文件描述符号给客户进程( 它的标准输出)。如果遇到了一个错误,那么通过使用我们之前描述的客户服务协议,send_err会被调用来将一个错误消息发送回去。
#include "opend.h"
#include <fcntl.h>
void request(char *buf, int nread, int fd)
{
int newfd;
if (buf[nread-1] != 0) {
sprintf(errmsg, "request not null terminated: %*.*s\n",
nread, nread, buf);
send_err(fd, -1, errmsg);
return;
}
if (buf_args(buf, cli_args) < 0) { /* parse args & set options */
send_err(fd, -1, errmsg);
return;
}
if ((newfd = open(pathname, oflag)) < 0) {
sprintf(errmsg, "can't open %s: %s\n", pathname,
strerror(errno));
send_err(fd, -1, errmsg);
return;
}
if (send_fd(fd, newfd) < 0) /* send the descriptor */
err_sys("send_fd error");
close(newfd); /* we're done with descriptor */
}
buf_args函数
客户请求是一个以null结束的,空白分割的字符串。下面的函数buf_args会将这个字符串变成标准argv形式的参数列表,然后调用用户函数处理相应的参数。本章后面我们将要使用buf_args函数。我们使用ISO的标准C函数strtok来将字符串分割成各个参数。
#include "apue.h"
#define MAXARGC 50 /* max number of arguments in buf */
#define WHITE " \t\n" /* white space for tokenizing arguments */
/*
* buf[] contains white-space-separated arguments. We convert it to an
* argv-style array of pointers, and call the user's function (optfunc)
* to process the array. We return -1 if there's a problem parsing buf,
* else we return whatever optfunc() returns. Note that user's buf[]
* array is modified (nulls placed after each token).
*/
int buf_args(char *buf, int (*optfunc)(int, char **))
{
char *ptr, *argv[MAXARGC];
int argc;
if (strtok(buf, WHITE) == NULL) /* an argv[0] is required */
return(-1);
argv[argc = 0] = buf;
while ((ptr = strtok(NULL, WHITE)) != NULL) {
if (++argc >= MAXARGC-1) /* -1 for room for NULL at end */
return(-1);
argv[argc] = ptr;
}
argv[++argc] = NULL;
/*
* Since argv[] pointers point into the user's buf[],
* user's function can just copy the pointers, even
* though argv[] array will disappear on return.
*/
return((*optfunc)(argc, argv));
}
cli_args函数
被buf_args调用的服务进程的函数是cli_args。它会检查客户进程发送了正确的促使数目,然后将其中的路径和打开模式存放在全局的变量中去。函数代码如下:
#include "opend.h"
/*
*This function is called by buf_args(), which is called by
*request(). buf_args() has broken up the client's buffer
*into an argv[]-style array, which we now process.
*/
int cli_args(int argc, char **argv)
{
if (argc != 3 || strcmp(argv[0], CL_OPEN) != 0) {
strcpy(errmsg, "usage: <pathname> <oflag>\n");
return(-1);
}
pathname = argv[1]; /* save ptr to pathname to open */
oflag = atoi(argv[2]);
return(0);
}
这样就完成了打开文件服务进程,这个进程使用客户进程的fork和exec运行。fork之前会创建一个单个的s-pipe,然后使用这个s-pipe来在客户进程和服务进程之间进行通信。通过这个架构,对与每个客户,我们都有一个服务。
网友评论