Linux下IPC机制之Socket通信总结
Linux下IPC机制有很多种,Socket算得上比较广泛的一种,在不使用像D-Bus之类的重量级消息总线之前采用socket作为两个进程之间的通话算得上比较不错的选择,因此它的用途比较广泛.这里稍微做下总结吧.
1:常规用法
[cpp][/cpp] view plaincopy
- //初始化MyLink进程
- int initMylinkMsgServer()
- {
- #ifdef LINUX_EVN
- pthread_mutex_init(&my_link_fd_mutex, NULL);
- static pthread_t tServer;
- if(pthread_create(&tServer, NULL, mylinkMsgServer,NULL) != 0)
- {
- pError(“\n initMylinkMsgServer error!\n”);
- return -1;
- }
- #endif
- return 0;
- }
[cpp][/cpp] view plaincopy
- //MirrorLink线程
- void *mylinkMsgServer(void * arg)
- {
- #ifdef LINUX_EVN
- socklen_t clt_addr_len;
- int ret;
- int len;
- struct sockaddr_un clt_addr;
- struct sockaddr_un srv_addr;
- int server_sockfd;
- //pthread_t rid;
- int *cfd;
- server_sockfd = socket(PF_UNIX, SOCK_STREAM, 0);<span style=”white-space:pre”> </span>//创建本地SOCKET
- if(server_sockfd < 0){
- pError(“cannot create communication socket!\n”);
- return ;
- }
- //set server addr_param
- srv_addr.sun_family = AF_UNIX;
- strncpy(srv_addr.sun_path, MY_SOCKET_PATH, sizeof(srv_addr.sun_path) – 1);
- unlink(MY_SOCKET_PATH);
- //bind sockfd & addr
- ret = bind(server_sockfd, (struct sockaddr*)&srv_addr, sizeof(srv_addr)); //绑定SOCKET
- if(ret == -1){
- pError(“cannot bind server socket!\n”);
- close(server_sockfd);
- unlink(MY_SOCKET_PATH);
- return ;
- }
- //listen sockfd
- ret = listen(server_sockfd, 1);<span style=”white-space:pre”> </span>//监听SOCKET事件
- pError(“\nServer listen !\n”);
- if(ret == -1){
- pError(“cannot listen the client connect reques !\n”);
- //perror(“cannot listen the client connect request\n”);
- close(server_sockfd);
- unlink(MY_SOCKET_PATH);
- return ;
- }
- len = sizeof(clt_addr);
- static int old_cli_fd = -1;
- while(1)
- {
- pthread_t rid;
- new_cli_fd = accept( server_sockfd, ( struct sockaddr * )&( clt_addr ), &len );//接受连接请求
- if(new_cli_fd < 0){
- new_cli_fd = -1;
- pError(“fail to accpet!\n”);
- continue;
- }
- pError(“new connect [%d]\n”,new_cli_fd);
- if(old_cli_fd != -1)
- {
- old_cli_fd = -1;
- }
- int ret = pthread_create(&rid, NULL, &RecvFormClient, (void *)(&new_cli_fd));//需要针对这个连接创建一接收线程
- if(ret != 0)
- {
- debug(“[%d][%s]\n”,ret,strerror(ret));
- debug(“Client pthread_create error.\n”);
- }
- if(pthread_detach(rid))
- {
- debug(“Client pthread_detach error.\n”);
- }
- old_cli_fd = new_cli_fd;
- }
- close(server_sockfd);
- unlink(MY_SOCKET_PATH);
- pthread_exit((void *)1);
- #endif
- return;
- }
这里:
[cpp][/cpp] view plaincopy
- #define MY_SOCKET_PATH “/var/tmp/mylink.txt”
接收消息时采用单独线程:
[cpp][/cpp] view plaincopy
- //接收消息
- void *RecvFormClient(void *arg)
- {
- #ifdef LINUX_EVN
- MsgInfo_t msg;
- int s_fd = *(int *)arg;
- int num;
- //read and printf sent client info
- while(s_fd > 0){
- memset(&msg, 0, sizeof(MsgInfo_t));
- num = recv(s_fd, &msg, sizeof(MsgInfo_t), 0);
- if(-1 == num)
- {
- pError(“fail to receivel!\n”);
- close(s_fd);
- //*s_fd = -1;
- break;
- }
- else if(0 == num)
- {
- pError(“the connect has been closed!\n”);
- close(s_fd);
- //*s_fd = -1;
- break;
- }
- else
- {
- printf(“Server recv Event: [%d] dataLen:[%d]!\n”,msg.MsgType,num);
- int msgType=msg.MsgType;
- switch(msgType)
- {
- case WM_START:
- //…
- break;
- case xxx:
- //…
- break;
- default:
- //…
- break;
- }
- }
- }
- pthread_exit((void *)1);
- #endif
- return;
- }
而发送消息时,则可以直接发送:
[cpp][/cpp] view plaincopy
- //发送消息给MyLink进程
- ssize_t sendMsgToClient(MsgInfo_t *msg)
- {
- #ifdef LINUX_EVN
- ssize_t ret = 0;
- pthread_mutex_lock(&my_link_fd_mutex);
- if(-1 != new_cli_fd)
- {
- ret = send(new_cli_fd, msg, sizeof(MsgInfo_t), 0);
- }
- else
- {
- pError(“No MyLink\n”);
- }
- pthread_mutex_unlock(&my_link_fd_mutex);
- return ret;
- #endif
- }
此方法在Linux下的使用得比较普遍.
2 抽象命名法:
上述方法很好,但是存在一个前提,收发双方都必须对做为文件路径的标志必须具有读写权限,但是在Android的中间件下采用上述方法有可能行不通,因此Android比较严格的权限控制很容易造成无法通信,虽然可以通信一些其它方式来解决,但还是不如直接像第一种方式通信来得痛快.下面介绍的这种抽象命名法就是解决这种问题.
服务端示例:
hmi_server.h:
[cpp][/cpp] view plaincopy
- #ifndef __HMI_SERVER_H__
- #define __HMI_SERVER_H__
- #define DEBUG_MODE
- #define SERVER_NAME “@server_socket”
- #define EPOLL_SIZE 1024
- #define BUF_SIZE 1024
- #define EPOLL_RUN_TIMEOUT -1
- // Macros – exit in any error (eval < 0) case
- #define CHK(eval) if(eval < 0){perror(“eval”); exit(-1);}
- // Macros – same as above, but save the result(res) of expression(eval)
- #define CHK2(res, eval) if((res = eval) < 0){perror(“eval”); exit(-1);}
- #endif
hmi_server.c:
[cpp][/cpp] view plaincopy
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <stdio.h>
- #include <sys/un.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/epoll.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <time.h>
- #include “hmi_server.h”
- //int makeAddr(const char* name, struct sockaddr_un* pAddr, socklen_t* pSockLen)
- //{
- // int nameLen = strlen(name);
- // if (nameLen >= (int) sizeof(pAddr->sun_path) -1) /* too long? */
- // return -1;
- // pAddr->sun_path[0] = ‘\0’; /* abstract namespace */
- // strcpy(pAddr->sun_path+1, name);
- // pAddr->sun_family = AF_UNIX;
- // *pSockLen = 1 + nameLen + offsetof(struct sockaddr_un, sun_path);
- // return 0;
- //}
- static int setnonblocking(int sockfd)
- {
- CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
- return 0;
- }
- // *** Handle incoming message from clients
- static int handle_message(int client,struct epoll_event *ev)
- {
- char buf[BUF_SIZE], message[BUF_SIZE];
- int len;
- bzero(buf, BUF_SIZE);
- bzero(message, BUF_SIZE);
- if(ev->events&EPOLLERR || ev->events&EPOLLHUP)
- {
- printf(“Client with fd: %d closed! \n”, client);
- CHK(close(client));
- return 0;
- }
- #ifdef DEBUG_MODE
- printf(“Try to read from fd(%d)\n”, client);
- #endif
- CHK2(len,recv(client, buf, BUF_SIZE, 0));
- // zero size of len mean the client closed connection
- if(len == 0)
- {
- CHK(close(client));
- #ifdef DEBUG_MODE
- printf(“Client with fd: %d closed! \n”, client);
- #endif
- }
- else
- {
- buf[len] =’\0′;
- printf(“message:%s\n”, buf);
- }
- return 0;
- }
- int main()
- {
- int listener, client_sockfd;
- socklen_t server_len, client_len;
- struct sockaddr_un server_addr;
- struct sockaddr_un client_addr;
- static struct epoll_event ev, events[EPOLL_SIZE];
- ev.events = EPOLLIN | EPOLLET|EPOLLERR|EPOLLHUP;
- char message[BUF_SIZE];
- int epfd;
- clock_t tStart;
- int client, res, epoll_events_count;
- //delete the old server socket
- //unlink(“server_socket”);
- //create socket
- CHK2(listener, socket(AF_UNIX, SOCK_STREAM, 0));
- setnonblocking(listener);
- server_addr.sun_family = AF_UNIX;
- strcpy(server_addr.sun_path, SERVER_NAME);
- server_addr.sun_path[0]=0;
- server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);
- //makeAddr(“server_socket”, &server_addr, &server_len);
- CHK(bind(listener, (struct sockaddr *)&server_addr, server_len));
- CHK(listen(listener, 5));
- CHK2(epfd,epoll_create(EPOLL_SIZE));
- ev.data.fd = listener;
- CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));
- while(1)
- {
- int i;
- CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));
- tStart = clock();
- for(i = 0; i < epoll_events_count ; i++)
- {
- if(events[i].data.fd == listener)
- {
- CHK2(client,accept(listener, (struct sockaddr *) &client_addr, &client_len));
- setnonblocking(client);
- ev.data.fd = client;
- CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev));
- //clients_list.push_back(client);
- bzero(message, BUF_SIZE);
- res = sprintf(message, “my test”, client);
- CHK2(res, send(client, message, BUF_SIZE, 0));
- }
- else
- {
- CHK2(res,handle_message(events[i].data.fd,&events[i]));
- }
- }
- printf(“Statistics: %d events handled at: %.2f second(s)\n”, epoll_events_count, (double)(clock() – tStart)/CLOCKS_PER_SEC);
- }
- printf(“hmi_server stop\n”);
- close(listener);
- close(epfd);
- return 0;
- }
客户端示例代码:
[cpp][/cpp] view plaincopy
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <stdio.h>
- #include <sys/un.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/epoll.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <time.h>
- #include <pthread.h>
- #define SERVER_NAME “@server_socket” //@为占位符
- #define EPOLL_SIZE 1024
- #define BUF_SIZE 1024
- #define EPOLL_RUN_TIMEOUT -1
- #define CLIENT_RECORD_MAX 5
- #define CHK(eval) if(eval < 0){perror(“eval”); exit(-1);}
- #define CHK2(res, eval) if((res = eval) < 0){perror(“eval”); exit(-1);}
- static int setnonblocking(int sockfd)
- {
- CHK(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK));
- return 0;
- }
- static int listener =-1;
- static void *send_thread(void *param)
- {
- char send_buf[1024];
- while(1)
- {
- printf(“[client] input content to send:”);
- scanf(“%s”,send_buf);
- strncat(send_buf,”\r\n”,sizeof(send_buf));
- if(!strcmp(send_buf,”exit\r\n”))
- {
- exit(1);
- }
- if(listener >1)
- {
- write(listener, send_buf, strlen(send_buf));
- }
- else
- {
- printf(“[client] server already close!\r\n”);
- }
- }
- }
- int main()
- {
- socklen_t len;
- struct sockaddr_un address;
- int result;
- int epoll_events_count;
- int epfd;
- static struct epoll_event ev, events[EPOLL_SIZE];
- ev.events = EPOLLIN | EPOLLET|EPOLLERR|EPOLLHUP;
- CHK2(listener, socket(AF_UNIX, SOCK_STREAM, 0));
- setnonblocking(listener); //设置为非阻塞线程
- address.sun_family = AF_UNIX;
- strcpy(address.sun_path, SERVER_NAME);
- address.sun_path[0]=0;
- len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);
- CHK(connect(listener, (struct sockaddr*)&address, len));
- CHK2(epfd,epoll_create(EPOLL_SIZE));<span style=”white-space:pre”> </span>//创建一epoll来监听socket事件
- ev.data.fd = listener;
- CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));
- {
- pthread_t mid;
- int ret;
- ret = pthread_create(&mid,NULL,send_thread,NULL);
- if(ret != 0){
- printf(“[client] can’t creat hmi_int %s\n”,strerror(ret));
- exit(1);
- }
- }
- char recv_buf[1024];
- int nread =0;
- int i,res;
- while(1)
- {
- CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));
- for(i = 0; i < epoll_events_count ; i++)
- {
- if(events[i].data.fd == listener)
- {
- // if(ev.events&EPOLLHUP)
- // {
- // printf(“[client] server closed1!: %d \n”, listener);
- // CHK(close(listener));
- // listener =-1;
- // return 0;
- // }
- nread =recv(listener, recv_buf, sizeof(recv_buf), 0);
- if(nread <= 0)
- {
- printf(“[client] server closed2!: %d,nread=%d\n”, listener,nread);
- CHK(close(listener));
- listener =-1;
- return 0;
- }
- else
- {
- recv_buf[nread] =’\0′;
- printf(“[client] recv message:%s\n”, recv_buf);
- }
- }
- }
- }
- exit(0);
- }
3 MiniGUI的注册socket事件
这里之所以提出MiniGUI,那是因为在MiniGUI下可以将socket通信事件注册为窗口事件,利用窗口的消息队列来处理.
[cpp][/cpp] view plaincopy
- //导航socket初始化
- int NaviSocketInit(HWND hWnd)
- {
- //监听导航socket
- printf(“NaviSocketInit…\n”);
- if (!listen_socket_navi(hWnd))
- {
- printf (“listen navi socket error!\n”);
- return -1;
- }
- }
[cpp][/cpp] view plaincopy
- //监听导航socket
- BOOL listen_socket_navi (HWND hwnd)
- {
- #ifdef _SOCKET
- //创建一个监听socket,这里serv_listen是minigui API接口
- if((listen_fd_navi = serv_listen (LISTEN_SOCKET_NAVI))<0)
- {
- printf(“serv_listen err!\n”);
- return FALSE;
- }
- printf(“serv_listen OK\n”);
- printf (“listen_fd_navi is %d\n”,listen_fd_navi);
- //向miniGUI注册监听socket,RegisterListenFD是minigui接口
- if(!RegisterListenFD (listen_fd_navi,POLLIN,hwnd, NULL))
- {
- printf(“RegisterListenFD failed\r\n”);
- return FALSE;
- }
- printf(“RegisterListenFD OK!\r\n”);
- #endif
- return TRUE;
- }
使用RegisterListenFD函数向MiniGUI系统注册监听Socket事件后, 每当产生 socket事件时,都会产生一个类型为MSG_FDEVENT事件:
[cpp][/cpp] view plaincopy
- case MSG_FDEVENT:
- NaviFdEventFunc(hWnd, message, wParam,lParam);
- return 0;
[cpp][/cpp] view plaincopy

- //接收socket数据处理例程
- int NaviFdEventFunc(HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
- {
- //printf(“receive navi socket event!flag_navi:%d,LOWORD(wParam):%d\r\n”,flag_navi,LOWORD (wParam));
- #ifdef _SOCKET
- if(LOWORD(wParam) ==listen_fd_navi) /* 来自监听套接字 */
- {
- pid_t pid;
- uid_t uid;
- s_conn_fd_navi = serv_accept (listen_fd_navi, &pid, &uid);
- if (s_conn_fd_navi >= 0)
- {
- RegisterListenFD (s_conn_fd_navi, POLLIN, hWnd, NULL);
- printf(“navi new socket connect!:%d\n”,s_conn_fd_navi);
- }
- }
- else/* 来自已连接套接字 */
- {
- int ret =0;
- fd_recv = LOWORD(wParam);
- memset(socket_str_c,0,sizeof(socket_str_c));
- //printf(“try to read socket data,fd_recv:%d\r\n”,fd_recv);
- /* 处理来自客户的数据 */
- ret =sock_read_t (fd_recv,socket_str_c,sizeof(socket_str_c),0);
- //printf(“sock_read_t ret=%d\n”,ret);
- if(ret>0)
- {
- test_char_c[ret]=’\0′;
- printf (“navi socket receive:%s,fd=%d\n”,socket_str_c,fd_recv);
- if (!strcmp (socket_str_c,”Navi_To_HMI\r\n”)) //返回主界面
- {
- //…
- }
- else if(!strcmp (socket_str_c,”Start_Success\r\n”))//启动成功
- {
- //…
- }
- //else if(…)
- else
- {
- //…
- }
- }
- }
- #endif
- return 0;
- }
三种socket本地通信方法,仅供参考.