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

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值