Linux进程间通信方式--本地socket

  • 格式:doc
  • 大小:64.50 KB
  • 文档页数:8

1. //s_unix.c 2. #include 3. #include 4. #include 5. #include 6. #define UNIX_DOMAIN "/tmp/UNIX.domain" 7. int main(void) 8. { 9. socklen_t clt_addr_len; 10. int listen_fd; 11. int com_fd; 12. int ret; 13. int i; 14. static char recv_buf[1024]; 15. int len; 16. struct sockaddr_un clt_addr; 17. struct sockaddr_un srv_addr; 18. listen_fd=socket(PF_UNIX,SOCK_STREAM,0); 19. if(listen_fd<0) 20. { 21. perror("cannot create communication socket"); 22. return 1; 23. } 24. 25. //set server addr_param 26. srv_addr.sun_family=AF_UNIX; 27. strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1); 28. unlink(UNIX_DOMAIN); 29. //bind sockfd & addr 30. ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); 31. if(ret==-1) 32. { 33. perror("cannot bind server socket"); 34. close(listen_fd); 35. unlink(UNIX_DOMAIN); 36. return 1; 37. } 38. //listen sockfd 39. ret=listen(listen_fd,1); 40. if(ret==-1) 41. { 42. perror("cannot listen the client connect request"); 43. close(listen_fd); 44. unlink(UNIX_DOMAIN); 45. return 1; 46. } 47. //have connect request use accept 48. len=sizeof(clt_addr); 49. com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len); 50. if(com_fd<0) 51. { 52. perror("cannot accept client connect request"); 53. close(listen_fd); 54. unlink(UNIX_DOMAIN); 55. return 1; 56. } 57. //read and printf sent client info 58. printf("/n=====info=====/n"); 59. for(i=0;i<4;i++) 60. { 61. memset(recv_buf,0,1024); 62. int num=read(com_fd,recv_buf,sizeof(recv_buf)); 63. printf("Message from client (%d)) :%s/n",num,recv_buf); 64. } 65. close(com_fd); 66. close(listen_fd); 67. unlink(UNIX_DOMAIN); 68. return 0; 69.}

客户端: [cpp] view plaincopy

1. //c_unix.c 2. #include 3. #include 4. #include 5. #include 6. #define UNIX_DOMAIN "/tmp/UNIX.domain" 7. int main(void) 8. { 9. int connect_fd; 10. int ret; 11. char snd_buf[1024]; 12. int i; 13. static struct sockaddr_un srv_addr; 14.//creat unix socket 15. connect_fd=socket(PF_UNIX,SOCK_STREAM,0); 16. if(connect_fd<0) 17. { 18. perror("cannot create communication socket"); 19. return 1; 20. } 21. srv_addr.sun_family=AF_UNIX; 22. strcpy(srv_addr.sun_path,UNIX_DOMAIN); 23.//connect server 24. ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); 25. if(ret==-1) 26. { 27. perror("cannot connect to the server"); 28. close(connect_fd); 29. return 1; 30. } 31. memset(snd_buf,0,1024); 32. strcpy(snd_buf,"message from client"); 33.//send info server 34. for(i=0;i<4;i++) 35. write(connect_fd,snd_buf,sizeof(snd_buf)); 36. close(connect_fd); 37. return 0; 38.}

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

一、创建socket流程 (1)创建socket,类型为AF_LOCAL或AF_UNIX,表示用于进程通信: 创建套接字需要使用 socket 系统调用,其原型如下: int socket(int domain, int type, int protocol); 其中,domain 参数指定协议族,对于本地套接字来说,其值须被置为 AF_UNIX 枚举值;type 参数指定套接字类型,protocol 参数指定具体协议;type 参数可被设置为 SOCK_STREAM(流式套接字)或 SOCK_DGRAM(数据报式套接字),protocol 字段应被设置为 0;其返回值为生成的套接字描述符。

对于本地套接字来说,流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;数据报式套接字 (SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通 过外界网络,这些情况出现的概率很小。

二、命名socket。 SOCK_STREAM 式本地套接字的通信双方均需要具有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用 struct sockaddr_un 类型的变量。

struct sockaddr_un { sa_family_t sun_family; /* AF_UNIX */ char sun_path[UNIX_PATH_MAX]; /* 路径名 */ };

这里面有一个很关键的东西,socket进程通信命名方式有两种。一是普通的命名,socket会根据此命名创建一个同名的socket文件,客户 端连接的时候通过读取该socket文件连接到socket服务端。这种方式的弊端是服务端必须对socket文件的路径具备写权限,客户端必须知道 socket文件路径,且必须对该路径有读权限。

另外一种命名方式是抽象命名空间,这种方式不需要创建socket文件,只需要命名一个全局名字,即可让客户端根据此名字进行连接。后者的实现过程 与前者的差别是,后者在对地址结构成员sun_path数组赋值的时候,必须把第一个字节置0,即sun_path[0] = 0,下面用代码说明:

第一种方式: [cpp] view plaincopy 1. //name the server socket 2. server_addr.sun_family = AF_UNIX; 3. strcpy(server_addr.sun_path,"/tmp/UNIX.domain"); 4. server_len = sizeof(struct sockaddr_un); 5. client_len = server_len;

第二种方式: [cpp] view plaincopy

1. #define SERVER_NAME @socket_server

[cpp] view plaincopy 1. //name the socket 2. server_addr.sun_family = AF_UNIX; 3. strcpy(server_addr.sun_path, SERVER_NAME); 4. server_addr.sun_path[0]=0; 5. //server_len = sizeof(server_addr); 6. server_len = strlen(SERVER_NAME) + offsetof(struct sockaddr_un, sun_path);

其中,offsetof函数在#include 头文件中定义。因第二种方式的首字节置0,我们可以在命名字符串SERVER_NAME前添加一个占位字符串,例如: [cpp] view plaincopy

1. #define SERVER_NAME @socket_server

前面的@符号就表示占位符,不算为实际名称。 提示:客户端连接服务器的时候,必须与服务端的命名方式相同,即如果服务端是普通命名方式,客户端的地址也必须是普通命名方式;如果服务端是抽象命名方式,客户端的地址也必须是抽象命名方式。