UNIX TCP回射服务器/客户端(2):多进程服务器

    《Unix网络编程》这本书附带了许多短小精美的小程序,我在阅读此书的时候,将书上的代码按照自己的理解重写了一遍(大部分是抄书上的),加深一下自己的理解(纯看书太困了,呵呵)。此例子在Ubuntu10.04上测试通过。

    PS:程序里使用了包裹函数(首字母是大写的函数)和常量(所有字母都是大写的常量)的声明在my_unp.h文件中,定义在unp_base.c和unp_thread.c中,地址:http://blog.csdn.net/aaa20090987/article/details/8096701

    程序简介:这是一个传统的多进程服务器模型。当一个客户端连接上服务器时,服务器就产生一个子进程来与客户端进行通信。这种通信机制不但效率低,而且最大连接数会受到系统的最大子进程数的限制。但是它的实现相对简单,所以在早期的UNIX系统中比较流行。


上代码:

#include "my_unp.h"  
  
void str_echo(int sockfd)  
{  
    ssize_t n;  
    char buf[MAXLINE];  
  
again:  
    //从套接字中读取数据,写到buffer中去  
    //再将buffer中的数据写到套接字中去  
    while( (n=read(sockfd, buf, MAXLINE)) > 0 )  
        Writen(sockfd, buf, n);  
  
    //由于信号中断,没写或读成功任何数据  
    if( n<0 && errno==EINTR )  
        goto again;  
    else if( n < 0 )  
        error_quit("str_echo: read error");  
}  
  
//捕获并处理子进程的SIGCHLD信号  
void sig_child(int signo)  
{  
    pid_t pid;  
    int stat;  
    while( (pid=waitpid(-1, &stat, WNOHANG)) > 0 )  
        printf("child %d terminated\n", pid);  
    return;  
}  
  
int main(void)  
{  
    int listenfd, connfd;  
    pid_t childpid;  
    socklen_t clilen;  
    char buff[MAXLINE];  
    struct sockaddr_in cliaddr, servaddr;  
  
    //创建用于TCP协议的套接字   
    listenfd = Socket(AF_INET, SOCK_STREAM, 0);  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
    servaddr.sin_port = htons(SERV_PORT);  
  
    //把socket和socket地址结构联系起来    
    Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));  
    //开始监听LISTENQ端口  
    Listen(listenfd, LISTENQ);  
  
    //处理SIGCHLD信号,防止子进程变成僵死进程  
    Signal(SIGCHLD, sig_child);  
  
    while(1)  
    {  
        clilen = sizeof(cliaddr);  
  
        //接受一个来自客户端的连接    
        //如果没有连接请求,就使程序睡眠,直到有连接请求--这是accept函数的特性    
        //accept函数返回一个描述符,这个socket(connfd)用于同连接到的客户的通信   
        connfd = accept(listenfd, (SA*)&cliaddr, &clilen);  
  
        if( connfd < 0 )  
        {  
            //accetp()是慢系统调用,在信号产生时会中断其调用,   
            //并将errno变量设置为EINTR,此时应重新调用  
            if( errno == EINTR )  
                continue;  
            else  
                error_quit("accept error");  
        }  
  
        //产生一个子进程,让它处理与(某个客户端的)通信  
        childpid = Fork();   
        if( childpid == 0 )  
        {  
            Close(listenfd);  
            str_echo(connfd);  
            return 0;  
        }  
        //输出客户端的IP地址与端口号,还有处理(子)进程的PID  
        printf("connection from %s, port %d. process with clild %d\n",    
            Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buff, sizeof(buff)),    
            ntohs(cliaddr.sin_port), childpid);   
        //与客户端的通信由子进程来处理,所以关闭此套接字  
        Close(connfd);  
    }  
    return 0;  
}  
与其配套的客户端在这里:http://blog.csdn.net/aaa20090987/article/details/8462262


你可能感兴趣的:(UNIX TCP回射服务器/客户端(2):多进程服务器)