在UNIX和类UNIX系统中,socket
编程提供了一种机制,允许进程之间进行通信。其中,UNIX域套接字(UNIX domain socket)是一种特殊的套接字,用于同一台机器上的进程间通信(IPC)。UNIX域套接字可以使用两种类型的地址:路径名套接字(pathname socket)和抽象套接字(abstract socket)。
路径名套接字 vs 抽象套接字
- 路径名套接字:使用文件系统中的一个路径名来标识套接字。这种套接字在文件系统中有对应的条目,因此会受到文件权限的限制。
- 抽象套接字:通过在
sun_path
字段的第一个字节设置为null字节(\0
或0x00
)来标识。抽象套接字在文件系统中没有对应的条目,因此不会受到文件权限的限制。它们仅存在于内核中,并通过这个null字节前缀进行区分。
为什么要设置p_addr->sun_path[0] = 0x00;
当你设置p_addr->sun_path[0] = 0x00;
时,你实际上是在指示这个UNIX域套接字应该作为一个抽象套接字来处理。这样做有几个好处:
- 避免文件系统污染:抽象套接字不会在文件系统中留下任何痕迹,因此不会污染文件系统命名空间。
- 简化权限管理:由于抽象套接字不依赖于文件系统,因此不需要考虑文件权限问题。
- 名称空间隔离:由于抽象套接字的名称仅在内核中有效,因此它们提供了更好的名称空间隔离,减少了不同应用程序之间名称冲突的可能性。
注意事项
- 抽象套接字的名称长度限制可能因系统而异,但通常较短(例如,Linux中通常是108字节,包括null终止符)。
- 由于抽象套接字名称不在文件系统中,因此无法通过文件系统操作(如
ls
、rm
等)来查看或删除它们。它们仅在内核中有效,并在套接字关闭时自动销毁。 - 抽象套接字的名称在内核中是唯一的,但仅限于当前运行的进程。当进程终止时,其创建的抽象套接字名称将不再可用。
代码:
#include <stdio.h> #include <sys/socket.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/un.h> #include <netinet/tcp.h> #include "socket.h" #include <arpa/inet.h> int socket_set_timeout(int fd, unsigned long sec, unsigned long usec) { struct timeval tv; tv.tv_sec = sec; tv.tv_usec = usec; return setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, (socklen_t)sizeof(tv)); } static int socket_make_sockaddr_un(const char *name,int namespace_id, struct sockaddr_un *p_addr, socklen_t *alen) { memset(p_addr, 0, sizeof(*p_addr)); size_t namelen; namelen = strlen(name); if((namelen + 1) > sizeof(p_addr->sun_path)){ printf("name too long. \n"); return -1; } p_addr->sun_path[0] = 0x00; memcpy(p_addr->sun_path + 1, name, namelen); p_addr->sun_family = AF_LOCAL; *alen = namelen + offsetof(struct sockaddr_un, sun_path)+1; return 0; } int creat_client(char *server_address, int timeout) { int result, length; int sfd = -1; struct sockaddr_un addr; int on = 1; int attempt = 3; length = sizeof(addr); socket_make_sockaddr_un(server_address, 0, &addr, (socklen_t *)&length); sfd = socket(AF_UNIX, SOCK_STREAM, 0); if(sfd < 0) { printf("create local socket error %s \n", strerror(errno)); return -1; } setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)); setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); while(attempt --){ result = connect(sfd, (struct sockaddr *)&addr, length); if(result == 0) { break; } sleep(1); } socket_set_timeout(sfd, timeout, 0); return sfd; } int create_server(const char *server_address, int n) { int sfd = -1, ret = -1; socklen_t length; struct sockaddr_un addr; int err; err = socket_make_sockaddr_un(server_address, 0, &addr, &length); if(err < 0){ printf("socket make socket address unix error .\n"); goto out; } sfd = socket(AF_LOCAL, SOCK_STREAM, 0); if(sfd < 0){ printf("socket() %s", strerror(errno)); goto out; } ret = bind(sfd, (struct sockaddr *)&addr, length); if(ret < 0){ printf("socket bind error: %s", strerror(errno)); goto out; } ret = listen(sfd, n); if(ret < 0){ printf("listen: %s", strerror(errno)); goto out; } return sfd; out: if(sfd > 0){ close(sfd); } return ret; } static int unix_send_buffer(int sfd, char *buffer, int size) { return send(sfd, buffer, size, 0); } static int __unix_socket_send(int sfd, char *data, int size) { int ret = -2; ret = unix_send_buffer(sfd, data, size); if(ret != size){ goto out; } ret = 0; out: return ret; } int unix_socket_send(int sfd, char *data, int size) { int ret; ret = send(sfd, &size, sizeof(size), 0); if(ret != sizeof(size)){ fprintf(stderr, "recv data size [%d] .\n", size); return -1; } return __unix_socket_send(sfd, data, size); } char *__unix_socket_recv(int sfd, int length, int try_count) { int ret; char *p; if(length < 0){ return NULL; } p = malloc(length); if(!p){ return NULL; } memset(p, 0x00, length); while(try_count --){ ret = recv(sfd, p, length, 0); if(ret > 0){ break; } if(ret < 0){ printf("try %d recv length = %d : %s .\n", try_count, length, strerror(errno)); if(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR){ continue; } } } if(ret <= 0){ free(p); p = NULL; printf("recv length = %d : %s .\n", length, strerror(errno)); return NULL; } return p; } char *unix_socket_recv(int sfd, int try_count) { int size = 0; int ret; ret = recv(sfd, &size, sizeof(size), 0); if(ret != sizeof(size)){ fprintf(stderr, "recv data size [%d] .\n", size); return NULL; } return __unix_socket_recv(sfd, size, try_count); }