zoukankan      html  css  js  c++  java
  • Linux 网络编程详解六(多进程服务器僵尸进程解决方案)

    小结:在点对点p2p程序中,服务器端子程序退出,子进程会主动发送信号,关闭父进程,
    但是这种模式导致服务器只能支持一个客户端连接,本章节中使用新的框架,子进程退出,不主动发送信号关闭父进程,而是父进程安装SIGCHLD信号,wait()子进程。
    这样就支持了多客户端。
    僵尸进程解决方案
    1.忽略SIGCHLD信号,这样不会出现僵尸进程
    2.安装信号,父进程接收到SIGCHLD信号后,wait()子进程
    //头文件
    int server_socket();
    
    int client_socket();
    //服务器端
    #include "pub.h"
    
    int main(int arg,char *args[])
    {
        server_socket();
        return 0;
    }
    //客户端
    #include "pub.h"
    
    int main(int arg,char *args[])
    {
        client_socket();
        return 0;
    }
    //辅助类实现
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <signal.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include "pub.h"
    
    ssize_t readn(int fd, const void *buf, ssize_t count)
    {
        if (buf == NULL)
        {
            printf("readn() params is not correct !
    ");
            return -1;
        }
        //定义剩余字节数
        ssize_t lread = count;
        //定义辅助指针变量
        char *pbuf = (char *) buf;
        //定义每次读取的字节数
        ssize_t nread = 0;
        while (lread > 0)
        {
            nread = read(fd, pbuf, lread);
            if (nread == -1)
            {
                //read是可中断睡眠函数,需要屏蔽信号
                if (errno == EINTR)
                    continue;
                perror("read() err");
                return -1;
            } else if (nread == 0)
            {
                printf("peer read socket is closed !
    ");
                //返回已经读取的字节数
                return count - lread;
            }
            //重置剩余字节数
            lread -= nread;
            //辅助指针后移
            pbuf += nread;
        }
        return count;
    }
    
    ssize_t writen(int fd, const void *buf, ssize_t count)
    {
        if (buf == NULL)
        {
            printf("writen() params is not correct !
    ");
            return -1;
        }
        //定于剩余字节数
        ssize_t lwrite = count;
        //定义每次写入字节数
        ssize_t nwrite = 0;
        //定义辅助指针变量
        char *pbuf = (char *) buf;
        while (lwrite > 0)
        {
            nwrite = write(fd, pbuf, lwrite);
            if (nwrite == -1)
            {
                if (errno == EINTR)
                    continue;
                perror("write() err");
                return -1;
            } else if (nwrite == 0)
            {
                printf("peer write socket is closed !
    ");
                return count - lwrite;
            }
            //重置剩余字节数
            lwrite -= nwrite;
            //辅助指针变量后移
            pbuf += nwrite;
        }
        return count;
    }
    
    ssize_t recv_peek(int fd, const void *buf, ssize_t count)
    {
        if (buf == NULL)
        {
            printf("recv_peek() params is not correct !
    ");
            return -1;
        }
        ssize_t ret = 0;
        while (1)
        {
            //此处有多少读取多少,不一定ret==count
            ret = recv(fd, (void *)buf, count, MSG_PEEK);
            if (ret == -1 && errno == EINTR)
                continue;
            return ret;
        }
        return -1;
    }
    
    ssize_t mreadline(int fd, const void *buf, ssize_t count)
    {
        //定义剩余字节数
        ssize_t lread = count;
        //定义每次读取的字节数
        ssize_t nread = 0;
        //定义辅助指针变量
        char *pbuf = (char *) buf;
        int i = 0, ret = 0;
        while (1)
        {
            nread = recv_peek(fd, pbuf, count);
            printf("recv_peek() 执行!
    ");
            if (nread == -1)
            {
                perror("recv_peek() err");
                return -1;
            } else if (nread == 0)
            {
                //注意:这里不要返回已经读取字节数,增加了调用函数的判断,直接返回-1,让调用函数当错误处理
                printf("peer socket is closed !
    ");
                return -1;
            }
            for (i = 0; i < nread; i++)
            {
                if (pbuf[i] == '
    ')
                {
                    //这是一段报文
                    memset(pbuf, 0, count);
                    //从socket缓存区读取i+1个字节
                    ret = readn(fd, pbuf, i + 1);
                    if (ret != i + 1)
                        return -1;
                    return ret;
                }
            }
            //如果当前socket缓存区中没有
    ,
            //那么先判断自定义buf是否还有空间,如果没有空间,直接退出
            //如果有空间,先将当前socket缓存区中的数据读出来,放入buf中,清空socket缓存
            //继续recv,判断下一段报文有没有
    
            if (lread >= count)
            {
                printf("自定义buf太小了!
    ");
                return -1;
            }
            //读取当前socket缓存
            ret = readn(fd, pbuf, nread);
            if (ret != nread)
                return -1;
            lread -= nread;
            pbuf += nread;
        }
        return -1;
    }
    
    void handler(int sign)
    {
        if (sign == SIGCHLD)
        {
            printf("子进程退出 !
    ");
            wait(NULL);
        }
    }
    
    int server_socket()
    {
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
        {
            perror("socket() err");
            return -1;
        }
        //reuseaddr
        int optval = 1;
        if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))
                == -1)
        {
            perror("setsockopt() err");
            return -1;
        }
        //bind
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8080);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        if (bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
        {
            perror("bind() err");
            return -1;
        }
        //listen
        if (listen(listenfd, SOMAXCONN) == -1)
        {
            perror("listen()err");
            return -1;
        }
        pid_t pid = 0;
        //忽略SIGCHLD信号
        //signal(SIGCHLD,SIG_IGN);
        //安装信号
        if (signal(SIGCHLD, handler) == SIG_ERR)
        {
            printf("signal() failed !
    ");
            return -1;
        }
        while (1)
        {
            struct sockaddr_in peeraddr;
            socklen_t peerlen = sizeof(peeraddr);
            int conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen);
            printf("accept by %s
    ", inet_ntoa(peeraddr.sin_addr));
            if (conn == -1)
            {
                perror("accept() err");
                return -1;
            }
            pid = fork();
            if (pid == -1)
            {
                perror("fork() err");
                return -1;
            }
            //子进程接收数据
            if (pid == 0)
            {
                //关闭监听套接字
                close(listenfd);
                char buf[1024] = { 0 };
                int ret = 0;
                while (1)
                {
                    ret = mreadline(conn, buf, 1024);
                    if (ret == -1)
                    {
                        close(conn);
                        return -1;
                    }
                    //打印客户端数据
                    fputs(buf, stdout);
                    //把数据返回给客户端
                    writen(conn, buf, ret);
                    memset(buf, 0, sizeof(buf));
                }
            } else if (pid > 0)
            {
                close(conn);
            }
        }
        return 0;
    }
    
    int client_socket()
    {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
            perror("socket() err");
            return -1;
        }
        //bind
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8080);
        addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr))==-1)
        {
            perror("connect() err");
            return -1;
        }
        pid_t pid=0;
        pid=fork();
        if(pid==-1)
        {
            perror("fork() err");
            return -1;
        }
        char buf[1024]={0};
        int ret=0;
        //子进程发送数据
        if(pid==0)
        {
            while(fgets(buf,sizeof(buf),stdin)!=NULL)
            {
                ret=writen(sockfd,buf,strlen(buf));
                if(ret!=strlen(buf))
                    return -1;
                memset(buf,0,sizeof(buf));
            }
        }else if(pid>0)
        {
            //父进程接收数据
            while(1)
            {
                ret=mreadline(sockfd,buf,sizeof(buf));
                if(ret==-1)
                    return -1;
                //打印数据
                fputs(buf,stdout);
                memset(buf,0,sizeof(buf));
            }
        }
        return 0;
    }
    .SUFFIXES:.c .o
    CC=gcc
    SRCS=tec01.c
        pub.c
    OBJS=$(SRCS:.c=.o)
    EXEC=runc
    
    SRCS1=hello.c
        pub.c
    OBJS1=$(SRCS1:.c=.o)
    EXEC1=hello
    
    
    start:$(OBJS) $(OBJS1)
        $(CC) -o $(EXEC) $(OBJS)
        $(CC) -o $(EXEC1) $(OBJS1)
        @echo "-------OK----------"
    .c.o:
        $(CC) -Wall -g -o $@ -c $<
    clean:
        rm -f $(OBJS)
        rm -f $(EXEC)
        rm -f $(OBJS1)
        rm -f $(EXEC1)
  • 相关阅读:
    原生js中,call(),apply(),bind()三种方法的区别
    jQuery回溯!!!!!!!
    java中异常类与类别
    Java 多线程(并发)
    Java中反射与常用方法
    漫谈计算机构成
    java语言特性(相比C++)
    初级排序算法学习笔记
    java中参数传递
    关于类的知识
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/6139544.html
Copyright © 2011-2022 走看看