红联Linux门户
Linux帮助

Linux下IPC机制之Socket通信总结

发布时间:2016-01-10 09:48:14来源:linux网站作者:flydream0

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


1、常规用法

//初始化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; 

//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; 

这里:

#define MY_SOCKET_PATH  "/var/tmp/mylink.txt" 

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

//接收消息 
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; 

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

//发送消息给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:

#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:

#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; 

客户端示例代码:

#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通信事件注册为窗口事件,利用窗口的消息队列来处理.

//导航socket初始化 
int NaviSocketInit(HWND hWnd) 

//监听导航socket 
printf("NaviSocketInit...\n"); 
if (!listen_socket_navi(hWnd)) 

printf ("listen navi socket error!\n"); 
return -1; 

//监听导航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事件:

case MSG_FDEVENT: 
NaviFdEventFunc(hWnd, message, wParam,lParam); 
return 0; 

//接收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本地通信方法,仅供参考.


使用SSH命令在Linux与Mac OS X搭建socket代理上网:http://www.linuxdiyf.com/linux/16945.html

socket在windows和Linux下的区别:http://www.linuxdiyf.com/linux/15016.html

Linux下数据传输(Socket)服务的测试工具及技巧:http://www.linuxdiyf.com/linux/4202.html

readn writen实现Linux下Socket缓冲区读写:http://www.linuxdiyf.com/linux/6024.html

RedHat5安装Socket5代理服务器:http://www.linuxdiyf.com/linux/10463.html