初识uds之abstract socket

发布时间 2023-11-19 11:00:51作者: SkyOnSky

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明

  无

前言


  在《记一次有趣的hwclock写RTC的PermissionDenied错误》(https://www.cnblogs.com/Iflyinsky/p/17841708.html 或者 https://https?/flyinskyin2013.github.io/2023/11/19/blog_idx_125/)中,我们提到了关于高通板卡,我们无法通过hwclock来写入rtc时钟。但是我们通过相关调查、测试、分析后发现,高通自己搞了一套新的方式来写入rtc时钟,这种特殊方法用的技术就是abstract socket。这也是我在工作中第一次遇到abstract socket,这里做一个简要的记录。

Abstract sockets


  

说明

  我们先不看代码,我们先看看abstract sockets具体是什么样子的。我们都知道,我们可以通过netstat查看unix socket相关的连接信息。我们来看看ubuntu 18.04里面用netstat查看unix socket信息如下:

rep_img

  注意图中我们看到的path那一列,除了普通的路径外,我们会看到一些奇怪的名字,每个名字前面都有一个@符号。这种特殊的名字,就是abstract sockets。



简介

  首先,根据文档,对于传统的uds来说,其支持两种类型,它们分别是:有名字的、未命名的。对于linux来说,这里还支持一种独立于文件系统的,且是一种不可移植的扩展,这就是我们要介绍的就是抽象的这种类型。

  对于抽象的uds来说,socket权限对其来说是没有意义的,例如umask、fchown、fchmod等不会影响其访问权限。

  对于抽象的uds来说,抽象的uds将会自动被销毁,当所有的引用打开的socket关闭的时候。

  对于抽象的uds来说,和有名字的uds最大的区别在于sockaddr_un.sun_path[0]是0x00,如果大家对c风格的字符串有印象,这是一个字符串的终止符。abstract uds的名字由sockaddr_un.sun_path的其余字节给出(这个时候的0x00是没有特殊意义的),其长度是addrlen - sizeof(sa_family_t)来定义的。



使用小例子

server

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
	int uds = socket(AF_UNIX, SOCK_STREAM, 0);
	if (uds < 0) perror("socket:");

	struct sockaddr_un addr;
	addr.sun_family = AF_UNIX;
	memset(addr.sun_path, 0x0, 108);
	const char * addr_path = "abstract_test";
	memcpy(addr.sun_path + 1, addr_path, strlen(addr_path));

	int ret = bind(uds, &addr, strlen(addr_path) + sizeof(sa_family_t) + 1);
	if (ret < 0) perror("bind:");

	ret = listen(uds, 2);
	if (ret < 0) perror("listen:");
	
	struct sockaddr_un r_addr;
	socklen_t r_addr_len;
	int _new = accept(uds, &r_addr, &r_addr_len);
	if (_new < 0) perror("accept:");
	char r_msg[256];
	memset(r_msg, 0x0, 256);
	ret = read(_new, r_msg, 256);
	if (ret < 0) perror("read:");
	printf("client msg = %s\n", r_msg);	
	write(_new, "hello client", sizeof("hello client"));

	sleep(5);

	close(uds);
	return 0;
}

client.c

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
	int uds = socket(AF_UNIX, SOCK_STREAM, 0);
	if (uds < 0) perror("socket:");

	struct sockaddr_un addr;
	addr.sun_family = AF_UNIX;
	memset(addr.sun_path, 0x0, 108);
	const char* addr_path = "abstract_test";
	memcpy(addr.sun_path + 1, addr_path, strlen(addr_path));


	int ret = connect(uds, &addr, strlen(addr_path) + sizeof(sa_family_t) + 1);
	if (ret < 0) perror("connect:");

	write(uds, "hello server", sizeof("hello server"));
	
	char r_msg[256];
	memset(r_msg, 0x0, 256);
	read(uds, r_msg, 256);
	printf("server msg %s\n", r_msg);	

	sleep(5);
	close(uds);
	return 0;
}

  首先我们撸了两个例子,一个是服务端,一个是客户端。

  首先我们运行客户端。然后通过netstat查看我们的socket。如下图:

rep_img

  当我们运行服务端后,再次运行客户端,就会看到我们传输的消息,如下图:

rep_img




后记


  通过我们如上的实验,可以看到其和普通的uds用法差不多,唯一的区别就是其不需要和文件系统绑定了。

  其实,这种不和文件系统绑定的特性,可以给我们带来更多有趣的用法(例如:不受文件系统权限影响进行通信),后续有缘分享。

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。