Linux下IPC机制之Socket通信总结

Linux下IPC机制有很多种,Socket算得上比较广泛的一种,在不使用像D-Bus之类的重量级消息总线之前采用socket作为两个进程之间的通话算得上比较不错的选择,因此它的用途比较广泛.这里稍微做下总结吧.

1:常规用法

 

[cpp][/cpp] view plaincopy

  1. //初始化MyLink进程
  2. int initMylinkMsgServer()
  3. {
  4. #ifdef LINUX_EVN
  5.     pthread_mutex_init(&my_link_fd_mutex, NULL);
  6.     static pthread_t tServer;
  7.     if(pthread_create(&tServer, NULL, mylinkMsgServer,NULL) != 0)
  8.     {
  9.         pError(“\n initMylinkMsgServer error!\n”);
  10.         return -1;
  11.     }
  12. #endif
  13.     return 0;
  14. }

 

[cpp][/cpp] view plaincopy

  1. //MirrorLink线程
  2. void *mylinkMsgServer(void * arg)
  3. {
  4. #ifdef LINUX_EVN
  5.     socklen_t clt_addr_len;
  6.     int ret;
  7.     int len;
  8.     struct sockaddr_un clt_addr;
  9.     struct sockaddr_un srv_addr;
  10.     int server_sockfd;
  11.     //pthread_t rid;
  12.     int *cfd;
  13.     server_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);<span style=”white-space:pre”>  </span>//创建本地SOCKET
  14.     if(server_sockfd < 0){
  15.         pError(“cannot create communication socket!\n”);
  16.         return ;
  17.     }
  18.     //set server addr_param
  19.     srv_addr.sun_family = AF_UNIX;
  20.     strncpy(srv_addr.sun_path, MY_SOCKET_PATH, sizeof(srv_addr.sun_path) – 1);
  21.     unlink(MY_SOCKET_PATH);
  22.     //bind sockfd & addr
  23.     ret = bind(server_sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)); //绑定SOCKET
  24.     if(ret == -1){
  25.         pError(“cannot bind server socket!\n”);
  26.         close(server_sockfd);
  27.         unlink(MY_SOCKET_PATH);
  28.         return ;
  29.     }
  30.     //listen sockfd
  31.     ret = listen(server_sockfd, 1);<span style=”white-space:pre”>               </span>//监听SOCKET事件
  32.     pError(“\nServer listen !\n”);
  33.     if(ret == -1){
  34.         pError(“cannot listen the client connect reques !\n”);
  35.         //perror(“cannot listen the client connect request\n”);
  36.         close(server_sockfd);
  37.         unlink(MY_SOCKET_PATH);
  38.         return ;
  39.     }
  40.     len = sizeof(clt_addr);
  41.     static int old_cli_fd = -1;
  42.     while(1)
  43.     {
  44.         pthread_t rid;
  45.         new_cli_fd = accept( server_sockfd, ( struct sockaddr * )&( clt_addr ), &len );//接受连接请求
  46.         if(new_cli_fd < 0){
  47.             new_cli_fd = -1;
  48.             pError(“fail to accpet!\n”);
  49.             continue;
  50.         }
  51.         pError(“new connect [%d]\n”,new_cli_fd);
  52.         if(old_cli_fd != -1)
  53.         {
  54.             old_cli_fd = -1;
  55.         }
  56.         int ret = pthread_create(&rid, NULL, &RecvFormClient, (void *)(&new_cli_fd));//需要针对这个连接创建一接收线程
  57.         if(ret != 0)
  58.         {
  59.             debug(“[%d][%s]\n”,ret,strerror(ret));
  60.             debug(“Client pthread_create error.\n”);
  61.         }
  62.         if(pthread_detach(rid))
  63.         {
  64.             debug(“Client pthread_detach error.\n”);
  65.         }
  66.         old_cli_fd = new_cli_fd;
  67.     }
  68.     close(server_sockfd);
  69.     unlink(MY_SOCKET_PATH);
  70.     pthread_exit((void *)1);
  71. #endif
  72.     return;
  73. }

这里:

 

 

[cpp][/cpp] view plaincopy

  1. #define MY_SOCKET_PATH  “/var/tmp/mylink.txt”

接收消息时采用单独线程:

 

 

[cpp][/cpp] view plaincopy
  1. //接收消息
  2. void *RecvFormClient(void *arg)
  3. {
  4. #ifdef LINUX_EVN
  5.     MsgInfo_t msg;
  6.     int s_fd = *(int *)arg;
  7.     int num;
  8.     //read and printf sent client info
  9.     while(s_fd > 0){
  10.         memset(&msg, 0, sizeof(MsgInfo_t));
  11.         num = recv(s_fd, &msg, sizeof(MsgInfo_t), 0);
  12.         if(-1 == num)
  13.         {
  14.             pError(“fail to receivel!\n”);
  15.             close(s_fd);
  16.             //*s_fd = -1;
  17.             break;
  18.         }
  19.         else if(0 == num)
  20.         {
  21.             pError(“the connect has been closed!\n”);
  22.             close(s_fd);
  23.             //*s_fd = -1;
  24.             break;
  25.         }
  26.         else
  27.         {
  28.             printf(“Server recv Event: [%d] dataLen:[%d]!\n”,msg.MsgType,num);
  29.             int msgType=msg.MsgType;
  30.             switch(msgType)
  31.             {
  32.                 case WM_START:
  33.                                      //…
  34.                                      break;
  35.                                 case xxx:
  36.                                      //…
  37.                                      break;
  38.                 default:
  39.                     //…
  40.                     break;
  41.             }
  42.         }
  43.     }
  44.     pthread_exit((void *)1);
  45. #endif
  46.     return;
  47. }

而发送消息时,则可以直接发送:

 

 

[cpp][/cpp] view plaincopy

  1. //发送消息给MyLink进程
  2. ssize_t sendMsgToClient(MsgInfo_t *msg)
  3. {
  4. #ifdef LINUX_EVN
  5.     ssize_t ret = 0;
  6.     pthread_mutex_lock(&my_link_fd_mutex);
  7.     if(-1 != new_cli_fd)
  8.     {
  9.         ret = send(new_cli_fd, msg, sizeof(MsgInfo_t), 0);
  10.     }
  11.     else
  12.     {
  13.         pError(“No MyLink\n”);
  14.     }
  15.     pthread_mutex_unlock(&my_link_fd_mutex);
  16.     return ret;
  17. #endif
  18. }

此方法在Linux下的使用得比较普遍.

 

 

 

2 抽象命名法:

上述方法很好,但是存在一个前提,收发双方都必须对做为文件路径的标志必须具有读写权限,但是在Android的中间件下采用上述方法有可能行不通,因此Android比较严格的权限控制很容易造成无法通信,虽然可以通信一些其它方式来解决,但还是不如直接像第一种方式通信来得痛快.下面介绍的这种抽象命名法就是解决这种问题.

 

服务端示例:

hmi_server.h:

 

[cpp][/cpp] view plaincopy

  1. #ifndef __HMI_SERVER_H__
  2. #define __HMI_SERVER_H__
  3. #define DEBUG_MODE
  4. #define SERVER_NAME “@server_socket”
  5. #define EPOLL_SIZE 1024
  6. #define BUF_SIZE 1024
  7. #define EPOLL_RUN_TIMEOUT -1
  8. // Macros – exit in any error (eval < 0) case
  9. #define CHK(eval) if(eval < 0){perror(“eval”); exit(-1);}
  10. // Macros – same as above, but save the result(res) of expression(eval)
  11. #define CHK2(res, eval) if((res = eval) < 0){perror(“eval”); exit(-1);}
  12. #endif

hmi_server.c:

 

 

[cpp][/cpp] view plaincopy

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <stdio.h>
  4. #include <sys/un.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <stddef.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/epoll.h>
  11. #include <fcntl.h>
  12. #include <errno.h>
  13. #include <time.h>
  14. #include “hmi_server.h”
  15. //int makeAddr(const char* name, struct sockaddr_un* pAddr, socklen_t* pSockLen)
  16. //{
  17. //    int nameLen = strlen(name);
  18. //    if (nameLen >= (int) sizeof(pAddr->sun_path) -1)  /* too long? */
  19. //        return -1;
  20. //    pAddr->sun_path[0] = ‘\0’;  /* abstract namespace */
  21. //    strcpy(pAddr->sun_path+1, name);
  22. //    pAddr->sun_family = AF_UNIX;
  23. //    *pSockLen = 1 + nameLen + offsetof(struct sockaddr_un, sun_path);
  24. //    return 0;
  25. //}
  26. static int setnonblocking(int sockfd)
  27. {
  28.     CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
  29.     return 0;
  30. }
  31. // *** Handle incoming message from clients
  32. static int handle_message(int client,struct epoll_event *ev)
  33. {
  34.     char buf[BUF_SIZE], message[BUF_SIZE];
  35.     int len;
  36.     bzero(buf, BUF_SIZE);
  37.     bzero(message, BUF_SIZE);
  38.     if(ev->events&EPOLLERR || ev->events&EPOLLHUP)
  39.     {
  40.         printf(“Client with fd: %d closed! \n”, client);
  41.         CHK(close(client));
  42.         return 0;
  43.     }
  44. #ifdef DEBUG_MODE
  45.         printf(“Try to read from fd(%d)\n”, client);
  46. #endif
  47.     CHK2(len,recv(client, buf, BUF_SIZE, 0));
  48.     // zero size of len mean the client closed connection
  49.     if(len == 0)
  50.     {
  51.         CHK(close(client));
  52. #ifdef DEBUG_MODE
  53.         printf(“Client with fd: %d closed! \n”, client);
  54. #endif
  55.     }
  56.     else
  57.     {
  58.         buf[len] =’\0′;
  59.         printf(“message:%s\n”, buf);
  60.     }
  61.     return 0;
  62. }
  63. int main()
  64. {
  65.     int listener, client_sockfd;
  66.     socklen_t server_len, client_len;
  67.     struct sockaddr_un server_addr;
  68.     struct sockaddr_un client_addr;
  69.     static struct epoll_event ev, events[EPOLL_SIZE];
  70.     ev.events = EPOLLIN | EPOLLET|EPOLLERR|EPOLLHUP;
  71.     char message[BUF_SIZE];
  72.     int epfd;
  73.     clock_t tStart;
  74.     int client, res, epoll_events_count;
  75.     //delete the old server socket
  76.     //unlink(“server_socket”);
  77.     //create socket
  78.      CHK2(listener, socket(AF_UNIX, SOCK_STREAM, 0));
  79.     setnonblocking(listener);
  80.     server_addr.sun_family = AF_UNIX;
  81.     strcpy(server_addr.sun_path, SERVER_NAME);
  82.     server_addr.sun_path[0]=0;
  83.     server_len = strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);
  84.     //makeAddr(“server_socket”, &server_addr, &server_len);
  85.     CHK(bind(listener, (struct sockaddr *)&server_addr, server_len));
  86.     CHK(listen(listener, 5));
  87.     CHK2(epfd,epoll_create(EPOLL_SIZE));
  88.     ev.data.fd = listener;
  89.     CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));
  90.     while(1)
  91.     {
  92.         int i;
  93.         CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));
  94.         tStart = clock();
  95.         for(i = 0; i < epoll_events_count ; i++)
  96.         {
  97.             if(events[i].data.fd == listener)
  98.             {
  99.                 CHK2(client,accept(listener, (struct sockaddr *) &client_addr, &client_len));
  100.                 setnonblocking(client);
  101.                 ev.data.fd = client;
  102.                 CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev));
  103.                 //clients_list.push_back(client);
  104.                 bzero(message, BUF_SIZE);
  105.                 res = sprintf(message, “my test”, client);
  106.                 CHK2(res, send(client, message, BUF_SIZE, 0));
  107.             }
  108.             else
  109.             {
  110.                 CHK2(res,handle_message(events[i].data.fd,&events[i]));
  111.             }
  112.         }
  113.         printf(“Statistics: %d events handled at: %.2f second(s)\n”, epoll_events_count, (double)(clock() – tStart)/CLOCKS_PER_SEC);
  114.     }
  115.     printf(“hmi_server stop\n”);
  116.     close(listener);
  117.     close(epfd);
  118.     return 0;
  119. }

客户端示例代码:

 

 

[cpp][/cpp] view plaincopy

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <stdio.h>
  4. #include <sys/un.h>
  5. #include <unistd.h>
  6. #include <stdlib.h>
  7. #include <stddef.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <sys/epoll.h>
  11. #include <fcntl.h>
  12. #include <errno.h>
  13. #include <time.h>
  14. #include <pthread.h>
  15. #define SERVER_NAME “@server_socket”    //@为占位符
  16. #define EPOLL_SIZE 1024
  17. #define BUF_SIZE 1024
  18. #define EPOLL_RUN_TIMEOUT -1
  19. #define CLIENT_RECORD_MAX 5
  20. #define CHK(eval) if(eval < 0){perror(“eval”); exit(-1);}
  21. #define CHK2(res, eval) if((res = eval) < 0){perror(“eval”); exit(-1);}
  22. static int setnonblocking(int sockfd)
  23. {
  24.     CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
  25.     return 0;
  26. }
  27. static  int listener =-1;
  28. static void *send_thread(void *param)
  29. {
  30.     char send_buf[1024];
  31.     while(1)
  32.     {
  33.         printf(“[client] input content to send:”);
  34.         scanf(“%s”,send_buf);
  35.         strncat(send_buf,”\r\n”,sizeof(send_buf));
  36.         if(!strcmp(send_buf,”exit\r\n”))
  37.         {
  38.             exit(1);
  39.         }
  40.         if(listener >1)
  41.         {
  42.             write(listener, send_buf, strlen(send_buf));
  43.         }
  44.         else
  45.         {
  46.             printf(“[client] server already close!\r\n”);
  47.         }
  48.     }
  49. }
  50. int main()
  51. {
  52.     socklen_t len;
  53.     struct sockaddr_un address;
  54.     int result;
  55.     int epoll_events_count;
  56.     int epfd;
  57.     static struct epoll_event ev, events[EPOLL_SIZE];
  58.     ev.events = EPOLLIN | EPOLLET|EPOLLERR|EPOLLHUP;
  59.     CHK2(listener, socket(AF_UNIX, SOCK_STREAM, 0));
  60.     setnonblocking(listener);                                //设置为非阻塞线程
  61.     address.sun_family = AF_UNIX;
  62.     strcpy(address.sun_path, SERVER_NAME);
  63.     address.sun_path[0]=0;
  64.     len =  strlen(SERVER_NAME)  + offsetof(struct sockaddr_un, sun_path);
  65.     CHK(connect(listener, (struct sockaddr*)&address, len));
  66.     CHK2(epfd,epoll_create(EPOLL_SIZE));<span style=”white-space:pre”>          </span>//创建一epoll来监听socket事件
  67.     ev.data.fd = listener;
  68.     CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));
  69.     {
  70.         pthread_t mid;
  71.         int ret;
  72.         ret = pthread_create(&mid,NULL,send_thread,NULL);
  73.         if(ret != 0){
  74.             printf(“[client] can’t creat hmi_int %s\n”,strerror(ret));
  75.             exit(1);
  76.         }
  77.     }
  78.     char recv_buf[1024];
  79.     int nread =0;
  80.     int i,res;
  81.     while(1)
  82.     {
  83.         CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));
  84.         for(i = 0; i < epoll_events_count ; i++)
  85.         {
  86.             if(events[i].data.fd == listener)
  87.             {
  88. //              if(ev.events&EPOLLHUP)
  89. //              {
  90. //                  printf(“[client] server closed1!: %d  \n”, listener);
  91. //                  CHK(close(listener));
  92. //                  listener =-1;
  93. //                  return 0;
  94. //              }
  95.                 nread =recv(listener, recv_buf, sizeof(recv_buf), 0);
  96.                 if(nread <= 0)
  97.                 {
  98.                     printf(“[client] server closed2!: %d,nread=%d\n”, listener,nread);
  99.                     CHK(close(listener));
  100.                     listener =-1;
  101.                     return 0;
  102.                 }
  103.                 else
  104.                 {
  105.                     recv_buf[nread] =’\0′;
  106.                     printf(“[client] recv message:%s\n”, recv_buf);
  107.                 }
  108.             }
  109.         }
  110.     }
  111.     exit(0);
  112. }

 

3 MiniGUI的注册socket事件

这里之所以提出MiniGUI,那是因为在MiniGUI下可以将socket通信事件注册为窗口事件,利用窗口的消息队列来处理.

 

[cpp][/cpp] view plaincopy
  1. //导航socket初始化
  2. int NaviSocketInit(HWND hWnd)
  3. {
  4.     //监听导航socket
  5.     printf(“NaviSocketInit…\n”);
  6.     if (!listen_socket_navi(hWnd))
  7.     {
  8.         printf (“listen navi socket error!\n”);
  9.         return -1;
  10.     }
  11. }

 

[cpp][/cpp] view plaincopy

  1. //监听导航socket
  2. BOOL listen_socket_navi (HWND hwnd)
  3. {
  4. #ifdef _SOCKET
  5.     //创建一个监听socket,这里serv_listen是minigui API接口
  6.     if((listen_fd_navi = serv_listen (LISTEN_SOCKET_NAVI))<0)
  7.     {
  8.         printf(“serv_listen err!\n”);
  9.         return FALSE;
  10.     }
  11.     printf(“serv_listen OK\n”);
  12.     printf (“listen_fd_navi is %d\n”,listen_fd_navi);
  13.     //向miniGUI注册监听socket,RegisterListenFD是minigui接口
  14.     if(!RegisterListenFD (listen_fd_navi,POLLIN,hwnd, NULL))
  15.     {
  16.         printf(“RegisterListenFD failed\r\n”);
  17.         return FALSE;
  18.     }
  19.     printf(“RegisterListenFD OK!\r\n”);
  20. #endif
  21.     return TRUE;
  22. }

使用RegisterListenFD函数向MiniGUI系统注册监听Socket事件后, 每当产生 socket事件时,都会产生一个类型为MSG_FDEVENT事件:

 

 

[cpp][/cpp] view plaincopy

  1. case MSG_FDEVENT:
  2. NaviFdEventFunc(hWnd, message, wParam,lParam);
  3.  return 0;

 

[cpp][/cpp] view plaincopy在CODE上查看代码片派生到我的代码片

  1. //接收socket数据处理例程
  2. int NaviFdEventFunc(HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
  3. {
  4.     //printf(“receive navi socket event!flag_navi:%d,LOWORD(wParam):%d\r\n”,flag_navi,LOWORD (wParam));
  5. #ifdef _SOCKET
  6.     if(LOWORD(wParam) ==listen_fd_navi) /* 来自监听套接字 */
  7.     {
  8.         pid_t pid;
  9.         uid_t uid;
  10.         s_conn_fd_navi = serv_accept (listen_fd_navi, &pid, &uid);
  11.         if (s_conn_fd_navi >= 0)
  12.         {
  13.             RegisterListenFD (s_conn_fd_navi, POLLIN, hWnd, NULL);
  14.             printf(“navi new socket connect!:%d\n”,s_conn_fd_navi);
  15.         }
  16.     }
  17.     else/* 来自已连接套接字 */
  18.     {
  19.         int ret =0;
  20.         fd_recv = LOWORD(wParam);
  21.         memset(socket_str_c,0,sizeof(socket_str_c));
  22.         //printf(“try to read socket data,fd_recv:%d\r\n”,fd_recv);
  23.         /* 处理来自客户的数据 */
  24.         ret =sock_read_t (fd_recv,socket_str_c,sizeof(socket_str_c),0);
  25.         //printf(“sock_read_t ret=%d\n”,ret);
  26.         if(ret>0)
  27.         {
  28.             test_char_c[ret]=’\0′;
  29.             printf (“navi socket receive:%s,fd=%d\n”,socket_str_c,fd_recv);
  30.             if (!strcmp (socket_str_c,”Navi_To_HMI\r\n”))   //返回主界面
  31.             {
  32.                            //…
  33.             }
  34.             else if(!strcmp (socket_str_c,”Start_Success\r\n”))//启动成功
  35.             {
  36.                //…
  37.             }
  38.                         //else if(…)
  39.                         else
  40.                         {
  41.                             //…
  42.                         }
  43.         }
  44.     }
  45. #endif
  46.     return 0;
  47. }

三种socket本地通信方法,仅供参考.

标签