写这篇博文,其实是最近看了很多类似的代码,想自己熟悉下这个流程。工作上有很多类似的进程间通信是用本地套接字来实现的,不同的进程来协作,每个进程处理一项任务,处理完,扔给下一个进程,而这个扔的过程就是通过本地套接字来发送数据完成的
进程间通信,UNP2 这本经典的书籍讲的已经很清楚了,常见的进程间通信就是信号量 消息队列 共享内存,其实本地套接字更是一种进程间通信的手段,而且起码要比信号量更加直观。本文描述下基本的利用本地套接字来进行进程通信。
- #define UNIX_PATH_MAX 108
-
- struct sockaddr_un {
- sa_family_t sun_family; /* AF_UNIX */
- char sun_path[UNIX_PATH_MAX]; /* pathname */
- };
可以看到这就是本地套接字的数据结构,110个字节,sun_path相当于socket的门牌号,或者地址,用于通信。我们知道,两个人通信,如果你知道对方的门牌号,那么你就能找到他,和他聊天。本地套接字也是一样,如果我是服务器,就在我门牌号这里等待别人来找我。
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<errno.h>
- #include<string.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<sys/socket.h>
- #include<sys/un.h>
- #include<signal.h>
- int pipe_process()
- {
- struct sigaction newact,oldact;
- newact.sa_handler = SIG_IGN;
- sigemptyset(&newact.sa_mask);
- newact.sa_flags = 0;
-
- sigaction(SIGPIPE,&newact,&oldact);
- return 0;
- }
- int main()
- {
- int server_sockfd,client_sockfd;
- int server_len,client_len;
- struct sockaddr_un server_address;
- struct sockaddr_un client_address;
- const char path_unix[] = "MY_SOCKET";
- int len_unix;
- char buf[1024];
- int n;
- unlink("MY_SOCKET");
- server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
- server_address.sun_family = AF_UNIX;
- strncpy(server_address.sun_path, path_unix,sizeof(server_address.sun_path));
- server_len = sizeof(server_address);
- len_unix = SUN_LEN(&server_address);
- bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
-
- pipe_process();
- listen(server_sockfd, 5);
- while(1){
- printf("server waiting\n");
-
- client_len = sizeof(client_address);
- client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len);
-
- memset(buf,0,sizeof(buf)/sizeof(buf[0]));
- n = read(client_sockfd,buf,1024);
- if(n < 0)
- {
- fprintf(stderr,"read failed\n");
- return -1;
- }
- fprintf(stderr,"recv : %s\n",buf);
- close(client_sockfd);
- }
- }
我们看下这几行代码- const char path_unix[] = "MY_SOCKET";
- unlink("MY_SOCKET");
- server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
- server_address.sun_family = AF_UNIX;
- strncpy(server_address.sun_path, path_unix,sizeof(server_address.sun_path));
- server_len = sizeof(server_address);
- len_unix = SUN_LEN(&server_address);
- bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
我先定义了一个门牌号,然后将我的套接字和门牌号绑定,这样的话,别人就能通过我的门牌号找到我了。这里插一段,SUN_LEN是一个宏来告知sockaddr_un的有效长度,对于本例子,len_unix = 2 + 9 =11.
bind之前- root@libin:~/program/C/sock/af_unix# netstat -ap|grep -Ei "serv|prot"
- Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
- Proto RefCnt Flags Type State I-Node PID/Program name 路径
- unix 2 [ ] 流 394869 4090/server
bind之后(当然也执行了listen)- root@libin:~/program/C/sock/af_unix# netstat -ap|grep -Ei "serv|prot"
- Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
- Proto RefCnt Flags Type State I-Node PID/Program name 路径
- unix 2 [ ACC ] 流 LISTENING 394869 4090/server MY_SOCKET
我们看到我们的套接字有了门牌号,这样客户端想要连接的话,就可以根据门牌号,按图索骥的找到我们了。
下面是客户端的代码:- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #define UNIX_DOMAIN "MY_SOCKET"
- int main(void)
- {
- int connect_fd;
- int ret;
- char snd_buf[1024];
- int i;
- static struct sockaddr_un srv_addr;
-
- connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
- if(connect_fd<0)
- {
- fprintf(stderr,"cannot create communication socket");
- return 1;
- }
- srv_addr.sun_family=AF_UNIX;
- strcpy(srv_addr.sun_path,UNIX_DOMAIN);
-
- ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
- if(ret==-1)
- {
- perror("cannot connect to the server");
- close(connect_fd);
- return 1;
- }
- memset(snd_buf,0,1024);
- strcpy(snd_buf,"hello server !");
-
- write(connect_fd,snd_buf,sizeof(snd_buf));
- close(connect_fd);
- return 0;
- }
可以看到,关键部分就是加粗的那部分代码,客户端根据sun_path就能找到服务器,然后去连接服务器。我们看到客户端给服务器进程发了个hello server就退出了。实际工程代码中,可以发送双方约定好的很复杂的数据,告诉服务器进程去处理客户端进程提交的数据。
OK,看下效果:
先启动服务器进程,可以看到服务器在等待客户来连接。
- root@libin:~/program/C/sock/af_unix# ./server
- server waiting
然后启动客户端进程,客户段打了招呼就退出了,- root@libin:~/program/C/sock/af_unix# ./client
- root@libin:~/program/C/sock/af_unix#
看下服务器的反应:服务器的确是收到了发来的语句,同时继续等待。- recv : hello server !
- server waiting
参考文献:
1 Linux socket program by example
阅读(6877) | 评论(0) | 转发(1) |