Chinaunix首页 | 论坛 | 博客
  • 博客访问: 265274
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2010-05-16 04:40:13

迭代服务器程序范式比较简单,核心步骤大致为:
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 (= 0; i < nchildren; i++) {
        if ( (pid = Fork()) == 0) { /* child */
            for (= 0; j < nloops; j++) {
                fd = Tcp_connect(argv[1], argv[2]);

                Write(fd, request, strlen(request));

                if ( (= 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) |
给主人留下些什么吧!~~