Chinaunix首页 | 论坛 | 博客
  • 博客访问: 836700
  • 博文数量: 91
  • 博客积分: 2544
  • 博客等级: 少校
  • 技术积分: 1885
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-12 09:08
文章存档

2016年(10)

2014年(2)

2013年(4)

2012年(23)

2011年(23)

2010年(13)

2009年(14)

2007年(2)

分类: LINUX

2009-07-13 15:27:52

2, 一客户一进程模型
    该模型在服务流程不太复杂和系统负担不太重的情况下使用。该模型为每一个客户请求生成一个子进程,由新生成的子进程为客户端服务,我们知道子进程拥有自己的进程空间,这样就可以避免各个子进程间相互影响。
    而且每个客户请求到来时,就新生成一个子进程,这样每个客户请求都可以及时的得到响应而不用等待。
    其流程如下:
    (1) 服务器端开启监听socket    
    (2) 有客户端请求到来,调到(3)
    (3) accept后,生成新的子进程,在子进程中关闭listen socket
    (4) 在子进程中为客户请求服务
    (5) 若还有客户请求到来,调到(2)
    
    该模型要注意的地方:
    1) 当子进程为一个客户请求服务完成后,需要回收子进程的资源。       
    3) 还有一个小地方要注意:accept可能被信号中断,这里需要处理一下
    while (1) {
        if (accept() < 0)
        if (errno == EINTR)
            continue;
    }

下面是代码原型,其中的readline和writen来自unpv3的代码。
下面是服务器端的代码:
----------------------------------code----------------------------------------------
/*
 * name : echo server.c
 * comment: echo string to client
 *             this version is paralled server using.
 */

#include "echosvr.h"
#include "util.h"

void str_echo(int sockfd);
void sig_chld(int signo);
void sig_chld2(int signo);

int
main(void)
{
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, svraddr;
   
    /* 用来处理死去的子进程,回收子进程资源 */
    signal(SIGCHLD, sig_chld2);

    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket()");
        exit(1);
    }   
    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* 可以连接服务器的任何网络接口 */
    svraddr.sin_port = htons(SVR_PORT);    /* 但必须是固定的服务端口 */

    if(bind(listenfd, (struct sockaddr *)&svraddr, sizeof(svraddr)) < 0){
        perror("bind()");
        exit(1);
    }
    if(listen(listenfd, LISTENQ) < 0){
        perror("listen()");
        exit(1);
    }
    for( ; ; ){
        clilen = sizeof(cliaddr);
        if((connfd = accept(listenfd,
                            (struct sockaddr *)&cliaddr,
                            &clilen)) < 0){
            /* 注意处理中断型号,让它重启 */
            if(errno == EINTR)
                continue;
            else{
                perror("accept()");
                exit(1);
            }
        }
        /* 当有连接到来时,生成一个新的子进程来处理客户请求 */
        if((childpid = fork()) == 0){   /* child */
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
       
        close(connfd);
    }
}

/* do all things for server */
void
str_echo(int sockfd)
{
    size_t n;
    char line[MAXLINE];   
    for( ; ; ){
        if((n = readline(sockfd, line, MAXLINE)) == 0)
            return ;
        writen(sockfd, line, n);
    }
}

/* 回收子进程资源 */
void sig_chld2(int signo)
{
    pid_t pid;
    int stat;
   
    while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);   
    return ;
}
----------------------------------edoc----------------------------------------------

下面是客户端的代码
----------------------------------code----------------------------------------------
/*
 * echo client .c
 *     echoclt version 3
 */
#include "echoclt.h"
#include "util.h"

void str_clt(FILE *fp, int sockfd);

int
main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in svraddr;
    int i;
   
    if(argc != 2){
        fprintf(stderr, "usage: %s \n", argv[0]);
        exit(1);
    }
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        perror("socket()");
        exit(1);
    }
    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(SVR_PORT);
    inet_pton(AF_INET, argv[1], &svraddr.sin_addr);
    if(connect(sockfd,
                (struct sockaddr *)&svraddr,
                sizeof(svraddr)) < 0){
        perror("connect()");
        exit(1);
    }
   
    str_clt(stdin, sockfd);
    exit(0);
}


/* 通过使用select我们可以探测到多个不同类型的描述符的读写情况 */
void
str_clt(FILE *fp, int sockfd)
{
    int maxfd;
    fd_set rset;
    char sendline[MAXLINE], rcvline[MAXLINE];
   
    FD_ZERO(&rset);
    for( ; ; ){
        FD_SET(fileno(fp), &rset);   /* set two fd */
        FD_SET(sockfd, &rset);
        maxfd = max(fileno(fp), sockfd) + 1;
        if(select(maxfd, &rset, NULL, NULL, NULL) < 0){
            fprintf(stderr, "select(): %s\n", strerror(errno));
            return;
        }
        /* 通过这一行,我们可以实时的得到消息:到服务器程序是否崩溃 */
        if(FD_ISSET(sockfd, &rset)){ /* socket is readable */
            if(readline(sockfd, rcvline, MAXLINE) == 0) {
                fprintf(stderr, " server terminated prematurely\n");
                return ;
            }
            fputs(rcvline, stdout);
        }
        if(FD_ISSET(fileno(fp), &rset)){ /* input is readable */
            if(fgets(sendline, MAXLINE, fp) == NULL)
                return;
            writen(sockfd, sendline, strlen(sendline));
        }
    }
}

----------------------------------edoc----------------------------------------------
以上例子来自unpv3。

参考书籍<>
阅读(2460) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~