下面给出的是客户程序用于测试我们的服务器程序的各个变体。
#include "unp.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 <hostname or IPaddr> <port> <#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);
}
13-15 每次运行本客户程序时,我们指定服务器的主机名或IP地址,服务器的端口号,由客户fork的子进程数(以运行客户并发的向同一个服务器发起多个连接),每个子进程发送给服务器的请求数,以及每个请求要求服务器返送的数据字节数。
22-38 父进程调用fork派生指定个数的子进程,每个子进程再与服务器建立指定数目的连接。每次建立连接之后,子进程就在该连接上向服务器发送一行文本,指出需由服务器返送多少字节的数据,然后再该连接上读入这个数量的数据,最后关闭该连接。父进程只有调用wait等待所有子进程都终止。需注意的是,这里关闭每个TCP连接的是客户端,因而TCP TIME_WAIT状态发生在客户端而不是服务器端。这是与通常的HTTP连接的差别之一。
我们在测试各个版本的服务器程序时,用于执行本客户程序的命令如下:
#client 206.62.226.36 8888 5 500 4000
这将建立2500个与服务器的TCP连接:5个子进程各自发起500次连接。在每个连接上,客户想服务器发送5字节数据("4000\n"),服务器向客户返送4000字节数据。我们在两个不同的主机上针对同一个服务器执行本客户程序,于是总共提供5000个TCP连接,而且任意时刻服务器端最多同时存在10个连接。