• 阐述linux IPC(两):基于socket进程间通信(下一个)


        【版权声明:尊重原创。转载请保留源:blog.csdn.net/shallnet 要么 .../gentleliu,文章学习交流。请勿用于商业用途】
        其中两个进程通信,有两个过程,以彼此的过程中交换信息,有的都比較复杂,不像上一节那样简单。

    普通情况下。存在一个服务进程一直在等待客户进程连接。客户进程和服务进程存在例如以下三种交换数据方式:
    客户进程发获取服务进程某一全局数据的请求,服务进程返回该数据(简称get请求);
    客户进程发设置服务进程全局数据的请求(简称set请求);
    客户进程发设置服务进程全局数据的请求,服务进程设置完毕后返回某一数据,(set与get并存)。
     
    这一节我们就来完毕这样一个能够在实际应用中使用的socket进程间通信。
    上面所描写叙述的get与set请求可能有非常多种,每一种请求所使用到的数据都不一样。所以我们须要为每一请求定义一个类型,服务进程通过类型能够知道客户进程须要处理什么数据。每个请求服务进程都应当返回其处理的结果返回值。既然是两个进程通信。那么就一定存在数据交换,所以一定存在数据的收发,以及须要知道对端收发数据的大小。所以我们依据这样的须要首先定义两个进程之间交换数据的数据结构(类似于tcp/ip协议数据包格式):

    typedef struct _ipc_sock_msg_t {
        int     msg_type;//请求类型
        int     msg_rc;//服务进程处理结果的返回值
        int     msg_buflen;//交换数据的大小
        char    msg_buf[SOCK_IPC_MAX_BUF];//交换数据的内容
    } ipc_sock_msg_t;
    服务进程收到客户进程请求之后首先推断请求类型,依据请求类型来进行处理。我们首先定义一个函数数组。在服务进程接收请求之前将要处理的全部请求注冊到该函数数组其中来,收到请求之后依据请求类型索引找到处理函数。
    函数数组例如以下:
    static int
    (*sln_ipc_ser_func[SLN_IPC_MAX_TYPE])(
            void *recvbuf, int recv_size,
            void *sendbuf, int *send_size);

    服务进程接收处理之前先将须要处理的函数注冊到函数数组中,例如以下:
    int sln_ipc_ser_func_init(void)
    {
        int     i;
     
        for (i = 0; i < SLN_IPC_MAX_TYPE; i++) {
            sln_ipc_ser_func[i] = NULL;
        }
     
        sln_ipc_ser_func[SLN_IPC_TYPE_0x1] = sln_ipc_handle_0x1;
        sln_ipc_ser_func[SLN_IPC_TYPE_0x2] = sln_ipc_handle_0x2;
     
        return 0;
    }

    之后服务进程開始监听,等待连接:
    监听代码类似上节演示样例:
    #if USE_AF_UNIX
        fd = sln_ipc_ser_afunix_listen(SOCK_IPC_NAME);
        if (fd < 0) {
            return -1;
        }
    #else
        fd = sln_ipc_ser_afinet_listen(SOCK_IPC_SER_LISTEN_PORT);
        if (fd < 0) {
            return -1;
        }
    #endif

    服务进程接收客户进程发送的数据,交给函数sln_ser_handle来处理:
    static int
    sln_ipc_ser_accept(int listenfd)
    {
        int                 connfd;
        ssize_t             recvlen;
        ipc_sock_msg_t      recv_msg;
        socklen_t           addrlen;
    #if USE_AF_UNIX
        struct sockaddr_un  cltaddr;
    #else
        struct sockaddr_in  cltaddr;
    #endif
     
        addrlen = sizeof(cltaddr);
        for (;;) {
            connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);
            if (connfd < 0) {
                fprintf(stderr, "accept: %s
    ", strerror(errno));
                continue;
            }
     
            if ((recvlen = sln_ipc_recv(connfd, &recv_msg, sizeof(ipc_sock_msg_t))) < 0) {
                continue;
            }
     
            sln_ser_handle(connfd, &recv_msg);
     
            close(connfd);
        }
     
        return 0;
    }

    当中处理函数sln_ser_handle实现为:
    static int
    sln_ser_handle(int sockfd, ipc_sock_msg_t *recv_msg)
    {
        ipc_sock_msg_t  send_msg;
     
        memset(&send_msg, 0, sizeof(ipc_sock_msg_t));
     
        send_msg.msg_type = recv_msg->msg_type;
       
        if ((recv_msg->msg_type >= SLN_IPC_MAX_TYPE)
            && (recv_msg->msg_rc < 0)) {
           send_msg.msg_rc = SLN_IPC_RC_TYPE;
        } else if (NULL == sln_ipc_ser_func[recv_msg->msg_type]) {
            send_msg.msg_rc = SLN_IPC_RC_FUNC;
        } else {
            send_msg.msg_rc
                = sln_ipc_ser_func[recv_msg->msg_type](
                        recv_msg->msg_buf,
                        recv_msg->msg_buflen,
                        send_msg.msg_buf,
                        &send_msg.msg_buflen);
        }
     
        if (sln_ipc_send(sockfd, &send_msg, sizeof(ipc_sock_msg_t)) < 0) {
            return -1;
        }
     
        return 0;
    }                    

    在函数sln_ser_handle中调用初始化时注冊的服务进程处理函数来完毕客户进程的请求。

    初始化注冊的两函数为:

    static char     gbuf[256] = "hello, this is server!";
     
    int
    sln_ipc_handle_0x1(void *recvbuf, int recv_size, void *sendbuf, int *send_size)
    {
        printf("=============%s->%d===========
    ", __func__, __LINE__);
        memcpy(sendbuf, gbuf, strlen(gbuf));
     
        *send_size = strlen(gbuf);
     
        return SLN_IPC_RC_OK;
    }
     
    int
    sln_ipc_handle_0x2(void *recvbuf, int recv_size, void *sendbuf, int *send_size)
    {
        printf("=============%s->%d===========
    ", __func__, __LINE__);
        memcpy(gbuf, recvbuf, recv_size);
     
        *send_size = 0;
     
        return SLN_IPC_RC_OK;
    }

    处理函数带4个參数,分别为:接收数据buffer、接收到的数据大小、发送数据buffer、发送数据大小。当中前两个參数为输入參数,后两个參数为返回输出的參数。服务进程须要依据客户进程发送过来的数据来处理客户请求,然后返回服务进程须要返回的数据以及处理结果(完毕或失败),处理结果在该处理函数的返回值中返回。


    以下看看客户进程的实现:

    int
    sln_ipc_clt_conn(
            int msg_type,
            int *ret_code,
            void *sendbuf,
            int sendlen,
            void *recvbuf,
            int *recvlen)
    {
        int                 connfd;
        ssize_t             ret_size;
        socklen_t           addrlen;
        ipc_sock_msg_t      send_msg, recv_msg;
     
    #if USE_AF_UNIX
        if ((connfd = sln_ipc_clt_afunix_conn_init(SOCK_IPC_NAME)) < 0) {
            return -1;
        }
        addrlen = sizeof(struct sockaddr_un);
    #else
        if ((connfd = sln_ipc_clt_afinet_conn_init(SOCK_IPC_SER_LISTEN_PORT)) < 0) {
            return -1;
        }
        addrlen = sizeof(struct sockaddr_in);
    #endif
     
        if (connect(connfd, (struct sockaddr *)&seraddr, addrlen) < 0) {
            fprintf(stderr,  "connect: %s
    ", strerror(errno));
            return -1;
        }
     
        memset(&send_msg, 0, sizeof(ipc_sock_msg_t));
        send_msg.msg_type = msg_type;
        if (NULL != sendbuf) {
            send_msg.msg_buflen = sendlen;
            memcpy(send_msg.msg_buf, sendbuf, sendlen);
        }
        if ((ret_size = ipc_send(connfd, &send_msg, 3 * sizeof(int) + sendlen)) < 0) {
            return -1;
        }
     
        if ((ret_size = ipc_recv(connfd, &recv_msg, sizeof(ipc_sock_msg_t))) < 0) {
            return -1;
        }
     
        if (recv_msg.msg_type != send_msg.msg_type) {
            printf("Error msg type!
    ");
            return -1;
        }
     
        *ret_code = recv_msg.msg_rc;
        if (NULL != recvbuf) {
            *recvlen = recv_msg.msg_buflen;
            memcpy(recvbuf, recv_msg.msg_buf, recv_msg.msg_buflen);
        }
     
        return 0;
    }

                           客户进程调用的接口的实现起来比較简单。仅仅须要告知服务进程我请求的处理类型(msg_type),以及须要传输的数据(sendbuf)及大小(sendlen),服务进程会返回处理结果(ret_code)以及返回数据(recvbuf)和大小(recvlen)。
    本节演示样例源代码:
    http://download.csdn.net/detail/gentleliu/8140479

    版权声明:本文博主原创文章,博客,未经同意不得转载。

    假设你认为你的实际物品。请点击以下“最佳”。

  • 相关阅读:
    cad是什么意思?教你快速把cad转换成pdf格式
    为什么街上的商贩更喜欢用微信支付,而不是支付宝,看完长知识了
    音乐剪辑软件怎么用?教你一个快速编辑音频的方法
    电脑如何录制视频?安利两种电脑录屏的方法
    被称为逆天改命的5大中国工程,曾轰动世界,你知道几个?
    如何使用音乐格式转换器?快速编辑音频文件的方法
    PPT结尾只会说“谢谢”?学会这些PPT结尾,观众主动为你鼓掌
    经典PHP面试题(冒泡排序),当场就被打脸,卧槽什么冒泡?为啥还排序?
    千万不要再搞混了,函数empty( var );输出的判断值是false : true
    PHP删除数组中空数组
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/4822201.html
Copyright © 2020-2023  润新知