什么是 Unix Domain Socket
一般将 socket 称为套接字,我们经常将 socket 指为网络通信的 socket,这些 socket 用于在网络中的不同设备之间的通信。但是 socket 也可以用于在同一台设备中(准确点,应该是同一 OS)的不用进程之间的通信,这样的 socket,被称为 unix domain socket
。
Unix Domain Socket 的特点
一般 unix domain socket
具有以下的特点:
-
unix domain socket
比 TCP、IP 在本地通信更快,因为unix domain socket
不经过网络协议栈,不需要打包、拆包、计算校验、维护序列号与应答等,只是将应用层数据从一个进程拷贝到另一个进程。 -
unix domain socket
也提供面向流与面向数据包的 API 接口,分别类似于 TCP 与 UDP。但是面向数据包的unix domain socket
也是可靠的,消息不会丢失,也不会乱序。 -
unix domain socket
是全双工。
Unix Domain Socket 的 API
创建 unix domain socket
的套接字,也是使用函数 socket
,但是与网络 socket 的入参有些不同,主要比较如下:
比较项 | 网络 socket | Unix Domain Socket |
---|---|---|
地址族 | AF_INET、PF_INET | AF_UNIX |
地址格式 | sockaddr_in(表示 IP 地址与 port 端口) | sockaddr_un(表示本地文件) |
type | SOCK_STREAM、SOCK_DGRAM、SOCK_RAW | SOCK_STREAM、SOCK_DGRAM、SOCK_RAW |
对于 unix domain socket
,绑定的地址不是网络 socket 的 IP 地址与端口号的形式,而是形式上为文件系统中的一个文件,但是该文件不是普通类型的文件,不能进行普通文件的读写操作,不能使用文本编辑器打开编辑,只能以 socket 的方式对其进行读写操作。比如:
[root@vm_rp0_cpu0_docker ~]# ll /tmp/
total 0
srwxr-x---. 1 root root 0 May 19 10:02 omci_cli_log
srwxr-x---. 1 root root 0 May 19 09:57 omci_log
地址格式比较为:
struct sockaddr_in {
sa_family_t sin_family; /* address family, AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sin_family; /* address family, AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* path name*/
};
对于路径名,分为两种,即普通路径名与抽象路径名。
- 普通路径名:是一个正常的字符串,也就是,sun_path 字段是以空字符结尾的绝对路径字符串;
- 抽象路径名:是 Linux 特有的一个特性,允许将一个 domain socket 绑定到一个名字上,而不会在文件系统中创建这个名字的 socket 文件。必须将 sun_path 的第一个字节设置为 NULL,即空字符,系统会将 sun_path 除了第一个字节之后余下的所有字节当作抽象名字,也就是说在解析抽象路径名时,需要用到 sun_path 中的所有字节。因为不会在文件系统中创建文件,对于抽象路径名来说,就不需要担心与文件系统中已经存在的文件产生名字冲突,也不需要在使用完套接字后删除附带的文件。当这个套接字被关闭后,系统自动删除这个抽象名。另外,抽象路径名还可以解决路径权限的问题。
Unix Domain Socket 的面向数据包的示例代码
服务端代码
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <iostream>
#include <unistd.h>
using namespace std;
char* server_file = "server.sock";
int main(int argc,char** argv)
{
int fd = socket(AF_UNIX,SOCK_DGRAM,0);
if (fd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_un addr;
memset(&addr,0,sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,server_file);
if (access(addr.sun_path,0) != -1)
{
remove(addr.sun_path);
}
if(bind(fd,(sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
return -1;
}
struct sockaddr_un clientaddr;
socklen_t len = sizeof(clientaddr);
char msgrecv[1024];
while (1)
{
memset(msgrecv,'\0',1024);
int size = recvfrom(fd,msgrecv,sizeof(msgrecv),0,(sockaddr*)&clientaddr,&len);
if (size < 0)
{
perror("recv");
return -1;
}
cout << "I'm server,receive a msg: " << msgrecv << " from: " << clientaddr.sun_path << endl;
if (strncmp("quit",msgrecv,4) == 0)
{
cout << "Server is exiting!" << endl;
break;
}
char *p = "OK,I got id!";
int ssize = sendto(fd,p,strlen(p),0,(sockaddr*)&clientaddr,len);
if (ssize < 0)
{
perror("sendto");
return -1;
}
sleep(1);
}
if (close(fd) < 0)
{
perror("close");
return -1;
}
return 0;
}
客户端代码
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <iostream>
#include <unistd.h>
using namespace std;
char* server_file = "server.sock";
char* client_file = "client.sock";
int main(int argc,char** argv)
{
int fd = socket(AF_UNIX,SOCK_DGRAM,0);
if (fd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_un addr;
memset(&addr,0,sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,client_file);
if (access(addr.sun_path,0) != -1)
{
remove(addr.sun_path);
}
if(bind(fd,(sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
return -1;
}
struct sockaddr_un clientaddr;
socklen_t len = sizeof(clientaddr);
char msgrecv[1024];
struct sockaddr_un serveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path,server_file);
char *p = "Hello,how are you?";
int ssize = sendto(fd,p,strlen(p),0,(sockaddr*)&serveraddr,len);
if (ssize < 0)
{
perror("sendto");
return -1;
}
int size = recvfrom(fd,msgrecv,sizeof(msgrecv),0,(sockaddr*)&serveraddr,&len);
if (size < 0)
{
perror("recv");
return -1;
}
cout << "I'm client,receive a msg :" << msgrecv << endl;
sleep(2);
char* goodbye = "quit";
if (sendto(fd,goodbye,strlen(goodbye),0,(sockaddr*)&serveraddr,len) < 0)
{
perror("sendto");
return -1;
}
if (close(fd) < 0)
{
perror("close");
return -1;
}
return 0;
}
网友评论