网络编程学习_简单的多进程并发服务器/客户端

本文是UNP第五章学习笔记

一,wait和waitpid函数

#include<sys/wait.h>

pid_t wait(int* statloc);

pid_t waitpid(pid_t pid,int statloc,int options);

这两个函数用来等待子进程结束. 参数statloc返回进程的终止状态,返回值为终止的子进程id

waitpid是wait的功能加强版,增加了一些有用的功能,因此也更为常用:

  1. pid函数指定了具体等待哪个子进程id, -1表示第一个结束的子进程
  2. options可以指定附加选项,常用的是WNOHANG,当没有已终止子进程时不阻塞
  3. 因为linux不对信号量进行队列处理,使用wait可能丢失信号量,推荐用waitpid+WNOHNG处理
二, 捕获信号量
网络编程用signal函数捕获一些信号量进行处理,
typedef void(*sig_t) ( int );
sig_t signal(int signum,sig_t handler);
signum即信号量,如SIGPIPE等, handler为信号处理函数,格式为 void handler(int signum);
handler还可以是 SIG_IGN(忽略)或 SIG_DFL(默认效果)

三,EINTR错误
如果你的程序正在捕获信号, 那么一些阻塞函数(例如read,write,accept等)可能会因捕获并处理信号导致返回一个错误值,同时将errno设置成EINTR, 通常的处理办法是重新执行,例如:

Again:
int newfd = accept(fd,NULL,NULL);
if(newfd<0 && errno==EINTR)
    goto Again;
......

四,SIGCHLD信号量
当子进程退出时,父进程会收到这个信号,默认效果是忽略. 这样的话子进程就会成为僵尸进程,多了的话可能导致占满进程号导致无法fork新的进程. 所以一般会处理掉.:
1是给SIGCHLD信号指定一个handler函数,在函数中使用waitpid结束子进程
void sig_chld(int signum)
{
    pid_t pid;
    while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
            printf("process %d close\n",pid);
}
2是干脆直接绑定SIGCHLD为SIG_IGN
signal(SIGCHLD,SIG_IGN);

五,SIGPIPE信号量
    这个信号量是当对方关闭了连接,而你第二次对这个连接进行写操作时产生的,(第一次写会返回0). 正确的流程是不会产生这一信号的,因为第一次write返回0以后你就应该关闭这个连接.这个信号会终止程序,而许多时候我们应该忽略它   signal(SIGPIPE,SIG_IGN); 
当你使用mysql等应用并产生大量连接时,最好忽略它,以免程序莫名其妙地退出

六,一个简单的多进程并发服务器程序示例
省略了一些出错处理
void sig_chld(int signo)
{
        pid_t pid;
        while((pid=waitpid(-1,NULL,WNOHANG))>0)
        {
                printf("child %d terminated\n",pid);
        }
        return ;
}
int main()
{
        int ret;
        int listenfd = socket(AF_INET,SOCK_STREAM,0);

        sockaddr_in addr;
        addr.sin_family=AF_INET;
        addr.sin_port=htons(8003);
        addr.sin_addr.s_addr=htonl(INADDR_ANY);
                                        printf("read ret =0,close socket\n");
                                        printf("read from newfd failed! ret=%d,%s\n",ret,strerror(errno));
        ret = bind(listenfd,(sockaddr*)&addr,sizeof(sockaddr_in));
        ret = listen(listenfd,5);
        setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, NULL, sizeof(int));
        int newfd;
        sockaddr_in client_addr;
        socklen_t client_addr_len;
        char* cli_ip;
        int cli_port;
        char recv_buf[100];
        char send_buf[100];

        signal(SIGCHLD,sig_chld);
        while ( (newfd = accept(listenfd,(sockaddr*)&client_addr,&client_addr_len)) >0 )
        {
                pid=fork();
                if(pid==0)
                {
                        close(listenfd);
                        while(1)
                        {
                                ret = read(newfd,recv_buf,sizeof(recv_buf));
                                if(ret<0)
                                {
                                        printf("read from newfd failed! ret=%d,%s\n",ret,strerror(errno));
                                        break;
                                }else if(ret == 0)
                                {
                                        close(newfd);
                                        break;
                                }else{
                                        recv_buf[ret]='\0';
                                        printf("recv %d bytes:[%s]\n",ret,recv_buf);
                                        strcpy(send_buf,"world");
                                        ret = write(newfd,send_buf,strlen(send_buf));
                                        printf("send %d bytes,[%s]\n",ret,send_buf);
                                }
                        }
                        close(newfd);
                }
                close(newfd);
        }
        return 0;
}


void sig_chld(int signum)
{
    pid_t pid;
    while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
            printf("process %d close\n",pid);
}



void sig_chld(int signum)
{
    pid_t pid;
    while( (pid=waitpid(-1,NULL,WNOHANG))>0 )
            printf("process %d close\n",pid);
}

你可能感兴趣的:(网络编程学习_简单的多进程并发服务器/客户端)