Chinaunix首页 | 论坛 | 博客
  • 博客访问: 31903
  • 博文数量: 7
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 12
  • 用 户 组: 普通用户
  • 注册时间: 2013-07-15 21:12
文章分类
文章存档

2014年(2)

2013年(5)

我的朋友

分类: LINUX

2013-08-10 12:59:31

原文地址:IPC之本地套接字 作者:Bean_lee


    写这篇博文,其实是最近看了很多类似的代码,想自己熟悉下这个流程。工作上有很多类似的进程间通信是用本地套接字来实现的,不同的进程来协作,每个进程处理一项任务,处理完,扔给下一个进程,而这个扔的过程就是通过本地套接字来发送数据完成的

    进程间通信,UNP2 这本经典的书籍讲的已经很清楚了,常见的进程间通信就是信号量 消息队列 共享内存,其实本地套接字更是一种进程间通信的手段,而且起码要比信号量更加直观。本文描述下基本的利用本地套接字来进行进程通信。

点击(此处)折叠或打开

  1. #define UNIX_PATH_MAX 108
  2.  
  3. struct sockaddr_un {
  4.        sa_family_t sun_family; /* AF_UNIX */
  5.        char sun_path[UNIX_PATH_MAX]; /* pathname */
  6. };
                    
    可以看到这就是本地套接字的数据结构,110个字节,sun_path相当于socket的门牌号,或者地址,用于通信。我们知道,两个人通信,如果你知道对方的门牌号,那么你就能找到他,和他聊天。本地套接字也是一样,如果我是服务器,就在我门牌号这里等待别人来找我。

    

点击(此处)折叠或打开

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<errno.h>
  5. #include<string.h>
  6. #include<sys/types.h>
  7. #include<sys/stat.h>
  8. #include<sys/socket.h>
  9. #include<sys/un.h>
  10. #include<signal.h>

  11. int pipe_process()
  12. {
  13.         struct sigaction newact,oldact;
  14.         newact.sa_handler = SIG_IGN;
  15.         sigemptyset(&newact.sa_mask);
  16.         newact.sa_flags = 0;
  17.         
  18.         sigaction(SIGPIPE,&newact,&oldact);
  19.         return 0;
  20. }

  21. int main()
  22. {
  23.                 int server_sockfd,client_sockfd;
  24.                 int server_len,client_len;
  25.                 struct sockaddr_un server_address;
  26.                 struct sockaddr_un client_address;
  27.                 const char path_unix[] = "MY_SOCKET";
  28.                 int len_unix;
  29.                 char buf[1024];
  30.                 int n;

  31.                 unlink("MY_SOCKET");
  32.                 server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

  33.                 server_address.sun_family = AF_UNIX;
  34.                 strncpy(server_address.sun_path, path_unix,sizeof(server_address.sun_path));
  35.                 server_len = sizeof(server_address);
  36.                 len_unix = SUN_LEN(&server_address);
  37.                 bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
  38.                 
  39.                 pipe_process();
  40.                 listen(server_sockfd, 5);
  41.                 while(1){

  42.                                 printf("server waiting\n");
  43.   
  44.                                 client_len = sizeof(client_address);
  45.                                 client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address, &client_len);
  46.                 
  47.                                 memset(buf,0,sizeof(buf)/sizeof(buf[0]));
  48.                                 n = read(client_sockfd,buf,1024);
  49.                              if(n < 0)
  50.                                 {
  51.                                  fprintf(stderr,"read failed\n");
  52.                                         return -1;
  53.                                 }
  54.                                 fprintf(stderr,"recv : %s\n",buf);
  55.                                 close(client_sockfd);
  56.                 }
  57. }
     我们看下这几行代码

点击(此处)折叠或打开

  1.        const char path_unix[] = "MY_SOCKET";


  2.         unlink("MY_SOCKET");
  3.         server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

  4.         server_address.sun_family = AF_UNIX;
  5.         strncpy(server_address.sun_path, path_unix,sizeof(server_address.sun_path));
  6.         server_len = sizeof(server_address);
  7.         len_unix = SUN_LEN(&server_address);
  8.         bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
    我先定义了一个门牌号,然后将我的套接字和门牌号绑定,这样的话,别人就能通过我的门牌号找到我了。这里插一段,SUN_LEN是一个宏来告知sockaddr_un的有效长度,对于本例子,len_unix = 2 + 9 =11.
 
                                 bind之前
  1. root@libin:~/program/C/sock/af_unix# netstat -ap|grep -Ei "serv|prot"
  2. Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
  3. Proto RefCnt Flags Type State I-Node PID/Program   name 路径
  4. unix    2     [ ]   流         394869 4090/server
                                  bind之后(当然也执行了listen)
  1. root@libin:~/program/C/sock/af_unix# netstat -ap|grep -Ei "serv|prot"
  2. Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
  3. Proto RefCnt Flags Type State    I-Node  PID/Program      name 路径
  4. unix   2    [ ACC ] 流 LISTENING 394869     4090/server   MY_SOCKET
     我们看到我们的套接字有了门牌号,这样客户端想要连接的话,就可以根据门牌号,按图索骥的找到我们了。

    下面是客户端的代码:
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <sys/un.h>
  5. #define UNIX_DOMAIN "MY_SOCKET"

  6. int main(void)
  7. {
  8.                 int connect_fd;
  9.                 int ret;
  10.                 char snd_buf[1024];
  11.                 int i;
  12.                 static struct sockaddr_un srv_addr;
  13.                 
  14.                 connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
  15.                 if(connect_fd<0)
  16.                 {
  17.                                 fprintf(stderr,"cannot create communication socket");
  18.                                 return 1;
  19.                 }
  20.                 srv_addr.sun_family=AF_UNIX;
  21.                 strcpy(srv_addr.sun_path,UNIX_DOMAIN);
  22.                 
  23.                 ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
  24.                 if(ret==-1)
  25.                 {
  26.                                 perror("cannot connect to the server");
  27.                                 close(connect_fd);
  28.                                 return 1;
  29.                 }
  30.                 memset(snd_buf,0,1024);
  31.                 strcpy(snd_buf,"hello server !");
  32.                 
  33.                 write(connect_fd,snd_buf,sizeof(snd_buf));
  34.                 close(connect_fd);
  35.                 return 0;
  36. }
    可以看到,关键部分就是加粗的那部分代码,客户端根据sun_path就能找到服务器,然后去连接服务器。我们看到客户端给服务器进程发了个hello server就退出了。实际工程代码中,可以发送双方约定好的很复杂的数据,告诉服务器进程去处理客户端进程提交的数据。

    OK,看下效果:
    先启动服务器进程,可以看到服务器在等待客户来连接。

  1. root@libin:~/program/C/sock/af_unix# ./server
  2. server waiting
    然后启动客户端进程,客户段打了招呼就退出了,

  1. root@libin:~/program/C/sock/af_unix# ./client
  2. root@libin:~/program/C/sock/af_unix#
   看下服务器的反应:服务器的确是收到了发来的语句,同时继续等待。

  1. recv : hello server !
  2. server waiting
参考文献:
1 Linux socket program by example


阅读(1745) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~