Linux 进程间通信--信号、TCP粘包处理

来源:互联网 发布:windows epub 阅读器 编辑:程序博客网 时间:2024/06/05 02:47

Server

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/in.h> #include <signal.h>#define ERR_EXIT(m)\        do{\            perror(m);\            exit(EXIT_FAILURE);\          }while(0)struct packet{    int len; //包头 存放包体中数据的实际长度    char buf[1024];};/**    进程由于系统调用或者中断进入内核    在从内核空间返回到用户空间之前,    检查是否有信号产生,跳到用户态执行信号处理函数    linux 信号处理机制,请见另篇博文的浅析    信号产生的时候 ,不会立即响应,这是和中断不一样的一个地方*/void handler(int sig){    exit(0);}ssize_t readn(int fd,void * buf,size_t count){    size_t nleft = count;    ssize_t nread;    char * bufp = (char *)buf;    while(nleft > 0)    {        nread = read(fd,bufp,nleft);        if (nread == -1)        {            /**                EINVAL 传给系统调用的参数不正确                EINTR  被信号中断                EFAULT 参数中有一指针指向无法存取的内存空间                信号会打断阻塞的系统调用。因此这里要判断一下。            */            if (errno == EINTR)//被信号中断            {                continue;            }else if (errno == EFAULT)//参数中有一指针指向无法存取的内存空间  会报"bad address"错误            {                printf("参数有错误\n");            }            return -1;        }else if (nread == 0) //对方关闭了连接        {            return count - nleft;        }else{            bufp += nread;            nleft -= nread; //剩余的字节数,如果大于0,继续读取        }       }    return count;}ssize_t writen(int fd, void * buf,size_t count){    size_t nleft = count;    ssize_t nwrite ;    char * bufp = (char *)buf;    while(nleft > 0)    {        if((nwrite = write(fd,bufp,nleft)) < 0)        {            if (errno == EINTR)            {                continue;               }else{                return -1;            }        }else if (nwrite == 0)        {            continue;        }else{            bufp += nwrite;            nleft -= nwrite;        }    }    return count;}void do_server(int conn){    struct packet recvbuf;    int n;    while(1)    {        memset(&recvbuf,0,sizeof(recvbuf));        /**            先接收包头        */        int ret = readn(conn,&recvbuf.len,4);        printf("packet header =%d \n",ret );        if (ret == -1)        {            ERR_EXIT("read");        }else if (ret < 4)        {            printf("client close\n");            break;        }        /**            接收包体        */        n = ntohl(recvbuf.len);        ret = readn(conn,&recvbuf.buf,n);        if (ret == -1)        {            ERR_EXIT("read");        }else if (ret < n)        {            printf("client close\n");            break;        }        fputs(recvbuf.buf,stdout);    }}int main(int argc, char const *argv[]){    int listenfd;    int ret;    int conn;    pid_t pid;    listenfd = socket(AF_INET,SOCK_STREAM,0);    if (listenfd == -1)    {        ERR_EXIT("socket");    }    struct sockaddr_in servaddr;    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(6666); //主机字节序转换成网络字节序    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);    int on = 1;    ret = setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));    if (ret == -1)    {        ERR_EXIT("setsockopt");    }    ret = bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));    if (ret == -1)    {        ERR_EXIT("bind");    }    //SOMAXCONN = 三次握手已完成 + 三次握手为完成    ret = listen(listenfd,SOMAXCONN);    if (ret == -1)    {        ERR_EXIT("listen");    }    struct sockaddr_in clientaddr;    socklen_t len = sizeof(clientaddr);    conn = accept(listenfd,(struct sockaddr *)&clientaddr,&len);    if (conn == -1)    {        ERR_EXIT("accept");    }    pid = fork();    if (pid == 0)  //子进程    {        //标准C 写法        //signal(SIGUSR1,handler);        struct sigaction act;        act.sa_handler = handler;        //将阻塞信号集全部置 0         sigemptyset(&act.sa_mask);        act.sa_flags = 0;         /**            int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);            signum : 你要为那个信号注册捕捉函数 : SIGKILL、SIGSTOP 除外(这两个不能被捕捉、阻塞、忽略).            act    : 新设定的动作            oldact : 信号原来的动作.输出参数            返回值 : 0  成功                      -1 失败               struct sigaction             {                早期的函数 与下面的一个函数指针互斥                通过sa_mask来选择采用那种捕捉函数                void (*sa_handler)(int);                 void (*sa_sigaction)(int ,siginfo_t *,void *);                临时信号屏蔽字:                在执行捕捉函数的时候,设置阻塞其他信号,                退出捕捉函数后,还原原有的阻塞信号集                sigset_t sa_mask;                int sa_flags;                void (*sa_restorer)(void);             };             */        int retval = sigaction(SIGUSR1,&act,NULL);        if (retval == -1)        {            ERR_EXIT("sigaction");        }        int n;        struct packet sendbuf;        memset(&sendbuf,0,sizeof(sendbuf));        while(fgets(sendbuf.buf,sizeof(sendbuf.buf),stdin) != NULL)        {            n = strlen(sendbuf.buf);            sendbuf.len = htonl(n);             writen(conn,&sendbuf,4 + n);             memset(&sendbuf,0,sizeof(sendbuf));        }        exit(0);    }else if (pid > 0)    {        while(1)        {            printf("---do_server\n");            do_server(conn);            break;        }        /**            向子进程发信号        */        kill(pid,SIGUSR1);        exit(0);    }else{        ERR_EXIT("fork");    }    close(conn);    close(listenfd);    return 0;}       

Client

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/in.h> #include <signal.h>#define ERR_EXIT(m)\        do{\            perror(m);\            exit(EXIT_FAILURE);\          }while(0)struct packet{    int len; //包头 存放包体中数据的实际长度    char buf[1024];};void handler(int sig){    exit(0);}ssize_t readn(int fd,void * buf,size_t count){    size_t nleft = count;    ssize_t nread;    char * bufp = (char *)buf;    while(nleft > 0)    {        nread = read(fd,bufp,nleft);        if (nread == -1)        {            /**                EINVAL 传给系统调用的参数不正确                EINTR  被信号中断                EFAULT 参数中有一指针指向无法存取的内存空间            */            if (errno == EINTR)//被信号中断            {                continue;            }else if (errno == EFAULT)//参数中有一指针指向无法存取的内存空间  会报"bad address"错误            {                printf("参数有错误\n");            }            return -1;        }else if (nread == 0) //对方关闭了连接        {            return count - nleft;        }else{            bufp += nread;            nleft -= nread; //剩余的字节数,如果大于0,继续读取        }       }    return count;}ssize_t writen(int fd, void * buf,size_t count){    size_t nleft = count;    ssize_t nwrite ;    char * bufp = (char *)buf;    while(nleft > 0)    {        if((nwrite = write(fd,bufp,nleft)) < 0)        {            if (errno == EINTR)            {                continue;               }else{                return -1;            }        }else if (nwrite == 0)        {            continue;        }else{            bufp += nwrite;            nleft -= nwrite;        }    }    return count;}void do_server(int conn){    struct packet recvbuf;    int n;    while(1)    {        memset(&recvbuf,0,sizeof(recvbuf));        /**            先接收包头        */        int ret = readn(conn,&recvbuf.len,4);        if (ret == -1)        {            ERR_EXIT("read");        }else if (ret < 4)        {            printf("client close\n");            break;        }        /**            接收包体        */        n = ntohl(recvbuf.len);        ret = readn(conn,&recvbuf.buf,n);           if (ret == -1)        {            ERR_EXIT("read");        }else if (ret < n)        {            printf("client close\n");            break;        }        fputs(recvbuf.buf,stdout);    }}int main(int argc, char const *argv[]){    int sock;    int ret;    pid_t pid;    sock = socket(AF_INET,SOCK_STREAM,0);    if (sock == -1)    {        ERR_EXIT("socket");    }    struct sockaddr_in servaddr;    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(6666);    servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");    ret = connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr));    if (ret == -1)    {        ERR_EXIT("connect");    }    pid = fork();    if (pid == 0)    {        //标准C 写法        //signal(SIGUSR1,handler);        struct sigaction act;        act.sa_handler = handler;        sigemptyset(&act.sa_mask);        act.sa_flags = 0;        sigaction(SIGUSR1,&act,NULL);        int n;        struct packet recvbuf;        memset(&recvbuf,0,sizeof(recvbuf));        while(fgets(recvbuf.buf,sizeof(recvbuf.buf),stdin) != NULL)        {            n = strlen(recvbuf.buf);            recvbuf.len = htonl(n);            writen(sock,&recvbuf,4 + n);            memset(&recvbuf,0,sizeof(recvbuf));        }        exit(0);    }else if (pid > 0)    {        struct packet recvbuf;        memset(&recvbuf,0,sizeof(recvbuf));        while(1)        {            do_server(sock);            break;        }        /**            向子进程发送信号        */        kill(pid,SIGUSR1);        exit(0);    }else{        ERR_EXIT("fork");    }    close(sock);    return 0;}       

这里写图片描述
这里写图片描述

0 0
原创粉丝点击