细说linux IPC(二):基于socket的进程间通信(下)

 【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流,请勿用于商业用途】
    在两个进程通信当中,存在两个进程相互交换信息的过程,有的都比较复杂,不像上一节那样简单。一般情况下,存在一个服务进程一直在等待客户进程连接,客户进程和服务进程存在如下三种交换数据方式:
客户进程发获取服务进程某一全局数据的请求,服务进程返回该数据(简称get请求);
客户进程发设置服务进程全局数据的请求(简称set请求);
客户进程发设置服务进程全局数据的请求,服务进程设置完成后返回某一数据,(set与get并存);
 
这一节我们就来完成这样一个可以在实际应用中使用的socket进程间通信。
上面所描述的get与set请求可能有很多种,每一种请求所使用到的数据都不一样,所以我们需要为每一请求定义一个类型,服务进程通过类型可以知道客户进程需要处理什么数据;每一个请求服务进程都应当返回其处理的结果返回值;既然是两个进程通信,那么就一定存在数据交换,所以一定存在数据的收发,以及需要知道对端收发数据的大小。所以我们根据这种需要首先定义两个进程之间交换数据的数据结构(类似于tcp/ip协议数据包格式):
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct _ipc_sock_msg_t {  
  2.     int     msg_type;//请求类型  
  3.     int     msg_rc;//服务进程处理结果的返回值  
  4.     int     msg_buflen;//交换数据的大小  
  5.     char    msg_buf[SOCK_IPC_MAX_BUF];//交换数据的内容  
  6. } ipc_sock_msg_t;  
服务进程收到客户进程请求之后首先判断请求类型,根据请求类型来进行处理。我们首先定义一个函数数组,在服务进程接收请求之前将要处理的所有请求注册到该函数数组当中来,收到请求之后根据请求类型索引找到处理函数。
函数数组如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int  
  2. (*sln_ipc_ser_func[SLN_IPC_MAX_TYPE])(  
  3.         void *recvbuf, int recv_size,  
  4.         void *sendbuf, int *send_size);  

服务进程接收处理之前先将需要处理的函数注册到函数数组中,如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int sln_ipc_ser_func_init(void)  
  2. {  
  3.     int     i;  
  4.    
  5.     for (i = 0; i < SLN_IPC_MAX_TYPE; i++) {  
  6.         sln_ipc_ser_func[i] = NULL;  
  7.     }  
  8.    
  9.     sln_ipc_ser_func[SLN_IPC_TYPE_0x1] = sln_ipc_handle_0x1;  
  10.     sln_ipc_ser_func[SLN_IPC_TYPE_0x2] = sln_ipc_handle_0x2;  
  11.    
  12.     return 0;  
  13. }  

之后服务进程开始监听,等待连接:
监听代码类似上节示例:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #if USE_AF_UNIX  
  2.     fd = sln_ipc_ser_afunix_listen(SOCK_IPC_NAME);  
  3.     if (fd < 0) {  
  4.         return -1;  
  5.     }  
  6. #else  
  7.     fd = sln_ipc_ser_afinet_listen(SOCK_IPC_SER_LISTEN_PORT);  
  8.     if (fd < 0) {  
  9.         return -1;  
  10.     }  
  11. #endif  

服务进程接收客户进程发送的数据,交给函数sln_ser_handle来处理:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int  
  2. sln_ipc_ser_accept(int listenfd)  
  3. {  
  4.     int                 connfd;  
  5.     ssize_t             recvlen;  
  6.     ipc_sock_msg_t      recv_msg;  
  7.     socklen_t           addrlen;  
  8. #if USE_AF_UNIX  
  9.     struct sockaddr_un  cltaddr;  
  10. #else  
  11.     struct sockaddr_in  cltaddr;  
  12. #endif  
  13.    
  14.     addrlen = sizeof(cltaddr);  
  15.     for (;;) {  
  16.         connfd = accept(listenfd, (struct sockaddr *)&cltaddr, &addrlen);  
  17.         if (connfd < 0) {  
  18.             fprintf(stderr, "accept: %s\n", strerror(errno));  
  19.             continue;  
  20.         }  
  21.    
  22.         if ((recvlen = sln_ipc_recv(connfd, &recv_msg, sizeof(ipc_sock_msg_t))) < 0) {  
  23.             continue;  
  24.         }  
  25.    
  26.         sln_ser_handle(connfd, &recv_msg);  
  27.    
  28.         close(connfd);  
  29.     }  
  30.    
  31.     return 0;  
  32. }  

其中处理函数sln_ser_handle实现为:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static int  
  2. sln_ser_handle(int sockfd, ipc_sock_msg_t *recv_msg)  
  3. {  
  4.     ipc_sock_msg_t  send_msg;  
  5.    
  6.     memset(&send_msg, 0, sizeof(ipc_sock_msg_t));  
  7.    
  8.     send_msg.msg_type = recv_msg->msg_type;  
  9.      
  10.     if ((recv_msg->msg_type >= SLN_IPC_MAX_TYPE)  
  11.         && (recv_msg->msg_rc < 0)) {  
  12.        send_msg.msg_rc = SLN_IPC_RC_TYPE;  
  13.     } else if (NULL == sln_ipc_ser_func[recv_msg->msg_type]) {  
  14.         send_msg.msg_rc = SLN_IPC_RC_FUNC;  
  15.     } else {  
  16.         send_msg.msg_rc  
  17.             = sln_ipc_ser_func[recv_msg->msg_type](  
  18.                     recv_msg->msg_buf,  
  19.                     recv_msg->msg_buflen,  
  20.                     send_msg.msg_buf,  
  21.                     &send_msg.msg_buflen);  
  22.     }  
  23.    
  24.     if (sln_ipc_send(sockfd, &send_msg, sizeof(ipc_sock_msg_t)) < 0) {  
  25.         return -1;  
  26.     }  
  27.    
  28.     return 0;  
  29. }                      

在函数sln_ser_handle中调用初始化时注册的服务进程处理函数来完成客户进程的请求。初始化注册的两函数为:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static char     gbuf[256] = "hello, this is server!";  
  2.    
  3. int  
  4. sln_ipc_handle_0x1(void *recvbuf, int recv_size, void *sendbuf, int *send_size)  
  5. {  
  6.     printf("=============%s->%d===========\n", __func__, __LINE__);  
  7.     memcpy(sendbuf, gbuf, strlen(gbuf));  
  8.    
  9.     *send_size = strlen(gbuf);  
  10.    
  11.     return SLN_IPC_RC_OK;  
  12. }  
  13.    
  14. int  
  15. sln_ipc_handle_0x2(void *recvbuf, int recv_size, void *sendbuf, int *send_size)  
  16. {  
  17.     printf("=============%s->%d===========\n", __func__, __LINE__);  
  18.     memcpy(gbuf, recvbuf, recv_size);  
  19.    
  20.     *send_size = 0;  
  21.    
  22.     return SLN_IPC_RC_OK;  
  23. }  

处理函数带4个参数,分别为:接收数据buffer、接收到的数据大小、发送数据buffer、发送数据大小。其中前两个参数为输入参数,后两个参数为返回输出的参数,服务进程需要根据客户进程发送过来的数据来处理客户请求,然后返回服务进程需要返回的数据以及处理结果(完成或失败),处理结果在该处理函数的返回值中返回。
下面看看客户进程的实现:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int  
  2. sln_ipc_clt_conn(  
  3.         int msg_type,  
  4.         int *ret_code,  
  5.         void *sendbuf,  
  6.         int sendlen,  
  7.         void *recvbuf,  
  8.         int *recvlen)  
  9. {  
  10.     int                 connfd;  
  11.     ssize_t             ret_size;  
  12.     socklen_t           addrlen;  
  13.     ipc_sock_msg_t      send_msg, recv_msg;  
  14.    
  15. #if USE_AF_UNIX  
  16.     if ((connfd = sln_ipc_clt_afunix_conn_init(SOCK_IPC_NAME)) < 0) {  
  17.         return -1;  
  18.     }  
  19.     addrlen = sizeof(struct sockaddr_un);  
  20. #else  
  21.     if ((connfd = sln_ipc_clt_afinet_conn_init(SOCK_IPC_SER_LISTEN_PORT)) < 0) {  
  22.         return -1;  
  23.     }  
  24.     addrlen = sizeof(struct sockaddr_in);  
  25. #endif  
  26.    
  27.     if (connect(connfd, (struct sockaddr *)&seraddr, addrlen) < 0) {  
  28.         fprintf(stderr,  "connect: %s\n", strerror(errno));  
  29.         return -1;  
  30.     }  
  31.    
  32.     memset(&send_msg, 0, sizeof(ipc_sock_msg_t));  
  33.     send_msg.msg_type = msg_type;  
  34.     if (NULL != sendbuf) {  
  35.         send_msg.msg_buflen = sendlen;  
  36.         memcpy(send_msg.msg_buf, sendbuf, sendlen);  
  37.     }  
  38.     if ((ret_size = ipc_send(connfd, &send_msg, 3 * sizeof(int) + sendlen)) < 0) {  
  39.         return -1;  
  40.     }  
  41.    
  42.     if ((ret_size = ipc_recv(connfd, &recv_msg, sizeof(ipc_sock_msg_t))) < 0) {  
  43.         return -1;  
  44.     }  
  45.    
  46.     if (recv_msg.msg_type != send_msg.msg_type) {  
  47.         printf("Error msg type!\n");  
  48.         return -1;  
  49.     }  
  50.    
  51.     *ret_code = recv_msg.msg_rc;  
  52.     if (NULL != recvbuf) {  
  53.         *recvlen = recv_msg.msg_buflen;  
  54.         memcpy(recvbuf, recv_msg.msg_buf, recv_msg.msg_buflen);  
  55.     }  
  56.    
  57.     return 0;  
  58. }  

                       客户进程调用的接口的实现起来比较简单,只需要告知服务进程我请求的处理类型(msg_type),以及需要传输的数据(sendbuf)及大小(sendlen),服务进程会返回处理结果(ret_code)以及返回数据(recvbuf)和大小(recvlen)。
本节示例源码:
http://download.csdn.net/detail/gentleliu/8140479
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值