当前位置:   article > 正文

[UNIX域Socket抽象命名空间(abstract_namespace)]_abstract namespace

abstract namespace

unix域 socket在*nix下是一种很受欢迎的IPC,不过有个小问题,它bind之后,会在文件系统中留下一个文件,然后再close之后文件却不会自动消失,这就导致下一次bind会失败,所以unix域socket的bind通常有个ulink凑热闹。

另一个问题是,这个文件很容易被其他程序不经意中删除,这导致很奇怪的问题,而很难发现。

下面要说的是如何在unix域socket中不使用文件系统路径,但保持接口的一致性(这种方法目前不具备特别好的移植性)。

简单地说,就是Linux在内存中维护了一个虚拟的"文件系统",这个文件在close之后会自动消失,并且文件系统中看不到bind的文件,用netstat -an却有记录。

和普通的unix域socket大部分地方一致,除了地址的设置。地址结构如下

  1. struct sockaddr_un
  2. {
  3. __SOCKADDR_COMMON (sun_);
  4. char sun_path[108]; /* Path name. */
  5. };

正常情况下,sun_path指定了需要bind(或者send/connect等)的路径,若使用abstract_namespace,那么sun_path[0]必须为'\0'(零字符)。同时在使用这个地址时,必须在addresslen参数中指定正确的长度,这个长度是sizeof(sun_family) + 1 + strlen(path)。其中path不包含后面的'\0',Linux也不会去寻找这个零字符。

当接收(accept,recvfrom等)数据时,Linux也不会在sun_path后面附上一个'\0',所以你必须根据返回的长度谨慎地处理字符串。

  1. #include "event.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <sys/un.h>
  8. #define SVR_PATH "/tmp/svr_path"
  9. #define CLI_PATH "/tmp/cli_path"
  10. struct sockaddr_un server_addr;
  11. int fd;
  12. int clifd;
  13. socklen_t addrlen;
  14. void udp_cb(int fd, short event, void *argc)
  15. {
  16. char buf[65536];
  17. char addr_buf_[sizeof(struct sockaddr_un)];
  18. struct sockaddr_un* cli_addr_ = (struct sockaddr_un*)addr_buf_;
  19. socklen_t socklen = sizeof(addr_buf_);
  20. int ret_ = recvfrom(
  21. fd,
  22. (void*)buf,
  23. sizeof(buf),
  24. 0,
  25. (struct sockaddr*)cli_addr_,
  26. &socklen);
  27. if(ret_ > 0)
  28. {
  29. // 注意根据长度来设置零字符
  30. cli_addr_->sun_path[socklen - 2] = '\0';
  31. printf("recv '%s' from '%s'\n",buf,cli_addr_->sun_path + 1);
  32. }
  33. }
  34. void udp_timer(int fd, short event, void *argc)
  35. {
  36. const char* msg_ = "hello world";
  37. printf("send msg %s\n",msg_);
  38. sendto(clifd,
  39. msg_,
  40. strlen(msg_) + 1,
  41. 0,
  42. (const struct sockaddr*)(&server_addr),
  43. addrlen);
  44. }
  45. int main()
  46. {
  47. struct event_base *base = event_init();
  48. struct timeval tv1 = {500,0};
  49. event_base_loopexit(base,&tv1);
  50. fd = socket(AF_UNIX,SOCK_DGRAM,0);
  51. server_addr.sun_family = AF_UNIX;
  52. server_addr.sun_path[0]=0;
  53. strcpy(server_addr.sun_path + 1,SVR_PATH);
  54. // 注意计算长度
  55. addrlen = sizeof(server_addr.sun_family) + sizeof(SVR_PATH);
  56. bind(fd, (struct sockaddr*)&server_addr, addrlen);
  57. clifd = socket(AF_UNIX,SOCK_DGRAM,0);
  58. struct sockaddr_un client_addr;
  59. client_addr.sun_family = AF_UNIX;
  60. client_addr.sun_path[0]=0;
  61. strcpy(client_addr.sun_path + 1,CLI_PATH);
  62. // 注意计算长度
  63. socklen_t addrlen_ = sizeof(client_addr.sun_family) + sizeof(CLI_PATH);
  64. bind(clifd, (struct sockaddr*)&client_addr, addrlen_);
  65. struct event timer_event_;
  66. struct timeval tv = {1,0};
  67. event_set(&timer_event_,
  68. -1,
  69. EV_PERSIST,
  70. udp_timer,
  71. 0);
  72. event_add(&timer_event_, &tv);
  73. /*libevent*/
  74. struct event svr_event_;
  75. event_set(
  76. &svr_event_,
  77. fd,
  78. EV_PERSIST | EV_READ,
  79. udp_cb,
  80. 0);
  81. event_add(&svr_event_, NULL);
  82. event_base_dispatch(base);
  83. return 0;
  84. }

  1. ~ <root@debian> 05:17:32 $ netstat -an
  2. unix 2 [ ] DGRAM 7260 @/tmp/cli_path
  3. unix 2 [ ] DGRAM 7259 @/tmp/svr_path

若使用netstat查询端口情况,通过abstract_namespace的方式创建的unix域socket bind之后,路径前有一个@

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/516002
推荐阅读
  

闽ICP备14008679号