迭代服务器程序范式比较简单,核心步骤大致为:
for ( ; ; ) {
connfd = accept(listenfd, cliaddr, &clilen)
...... /* deal with client's request */
Close(connfd);
}
服务器依次执行每一次客户请求,当一个请求没完成之前无法处理已等待服务的新客户。
迭代服务器的源代码如下,不再做过多解释:
serv00.c
------------------------
#include "global.h"
int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; void sig_int(int), web_child(int); socklen_t clilen, addrlen; struct sockaddr* cliaddr;
if (argc == 2) listenfd = Tcp_listen(NULL, argv[1], &addrlen); else if (argc == 3) listenfd = Tcp_listen(argv[1], argv[2], &addrlen); else err_quit("usage: serv01 [ ] "); cliaddr = Malloc(addrlen);
Signal(SIGINT, sig_int);
for ( ; ; ) { clilen = addrlen; if ( (connfd = accept(listenfd, cliaddr, &clilen)) < 0) { if (errno == EINTR) continue; /* back to for() */ else err_sys("accept error"); }
web_child(connfd); /* process request */
Close(connfd); /* parent closes connected socket */ } } /* end serv01 */
/* include sigint */ void sig_int(int signo) { void pr_cpu_time(void);
pr_cpu_time(); exit(0); } /* end sigint */
|
程序中的sig_int函数用于捕获由键入终端中断键产生的SIGINT信号,当客户程序运行完毕之后,我们键入该键以显示服务器程序运行所需的CPU时间。
服务器程序对业务的处理表现在web_child()函数里
web_child.c
---------------------------
#include "global.h"
#define MAXN 16384 /* max # bytes client can request */
void web_child(int sockfd) { int ntowrite; ssize_t nread; char line[MAXLINE], result[MAXN];
for ( ; ; ) { if ( (nread = Readline(sockfd, line, MAXLINE)) == 0) return; /* connection closed by other end */
/* 4line from client specifies #bytes to write back */ ntowrite = atol(line); if ((ntowrite <= 0) || (ntowrite > MAXN)) err_quit("client request for %d bytes", ntowrite);
Writen(sockfd, result, ntowrite); } }
|
发送ntowrite字节个任意的数据给客户程序。
计算服务器所用CPU时间的函数pr_cpu_time()也很简单,利用系统函数getrusage()得到程序运行的user time和sys time:
pr_cpu_time.c
------------------------------
#include "global.h" #include <sys/resource.h>
#ifndef HAVE_GETRUSAGE_PROTO int getrusage(int, struct rusage *); #endif
void pr_cpu_time(void) { double user, sys; struct rusage myusage, childusage;
if (getrusage(RUSAGE_SELF, &myusage) < 0) err_sys("getrusage error"); if (getrusage(RUSAGE_CHILDREN, &childusage) < 0) err_sys("getrusage error");
user = (double) myusage.ru_utime.tv_sec + myusage.ru_utime.tv_usec/1000000.0; user += (double) childusage.ru_utime.tv_sec + childusage.ru_utime.tv_usec/1000000.0; sys = (double) myusage.ru_stime.tv_sec + myusage.ru_stime.tv_usec/1000000.0; sys += (double) childusage.ru_stime.tv_sec + childusage.ru_stime.tv_usec/1000000.0;
printf("\nuser time = %g, sys time = %g\n", user, sys); }
|
下面主要介绍一下用于测试服务器程序(向服务器程序发送请求)的客户程序:
client.c
--------------------------------
#include "global.h"
#define MAXN 16384 /* max # bytes to request from server */
int main(int argc, char **argv) { int i, j, fd, nchildren, nloops, nbytes; pid_t pid; ssize_t n; char request[MAXLINE], reply[MAXN];
if (argc != 6) err_quit("usage: client <#children> " "<#loops/child> <#bytes/request>");
nchildren = atoi(argv[3]); nloops = atoi(argv[4]); nbytes = atoi(argv[5]); snprintf(request, sizeof(request), "%d\n", nbytes); /* newline at end */
for (i = 0; i < nchildren; i++) { if ( (pid = Fork()) == 0) { /* child */ for (j = 0; j < nloops; j++) { fd = Tcp_connect(argv[1], argv[2]);
Write(fd, request, strlen(request));
if ( (n = Readn(fd, reply, nbytes)) != nbytes) err_quit("server returned %d bytes", n);
Close(fd); /* TIME_WAIT on client, not server */ } printf("child %d done\n", i); exit(0); } /* parent loops around to fork() again */ }
while (wait(NULL) > 0) /* now parent waits for all children */ ; if (errno != ECHILD) err_sys("wait error");
exit(0); }
|
父进程调用fork派生指定个数的子进程,每个子进程再与服务器建立指定次数的连接。每次连接建立后,子进程就在该连接上向服务器发送一行文本,指出需由服务器返回多少字节的数据,然后在该连接上读入这个数量的数据,最后关闭该连接。父进程只是调用wait等待所有子进程终止。
在后面的测试中,我们都执行如下的客户程序命令:
$ ./client 173.26.100.162 12345 5 500 4000
5个子进程各自发起500次连接,总共建立2500个与服务器的TCP连接,在每个连接上,客户向服务器发送5个字节的数据(“4000\n”),服务器向客户返回4000字节的数据。
程序中还要很多包裹函数,分别在 error.c signal.c tcp_listen.c tcp_connect.c wrapsock.c wrapunix.c wrappthread.c writen.c readn.c readline.c read_fd.c write_fd.c中,这里就不一一解释了,读者可以下载附件中的打包程序,自行阅读。
最后,迭代服务器的执行结果如下:
$ ./serv00 173.26.100.162 12345
在另一个shell窗口中
$ ./client 173.26.100.162 12345 5 500 4000
child 0 done
child 1 done
child 2 done
child 3 done
child 4 done
回到服务程序shell窗口,ctrl+c发送中断信号
$ ./serv00 173.26.100.162 12345
user time = 0.004, sys time = 0.084005
|
文件: | ServerClient.tar.gz |
大小: | 14KB |
下载: | 下载 |
|
阅读(1196) | 评论(0) | 转发(0) |