Linux抽象套接字

在UNIX和类UNIX系统中,socket编程提供了一种机制,允许进程之间进行通信。其中,UNIX域套接字(UNIX domain socket)是一种特殊的套接字,用于同一台机器上的进程间通信(IPC)。UNIX域套接字可以使用两种类型的地址:路径名套接字(pathname socket)和抽象套接字(abstract socket)。

路径名套接字 vs 抽象套接字

  • 路径名套接字:使用文件系统中的一个路径名来标识套接字。这种套接字在文件系统中有对应的条目,因此会受到文件权限的限制。
  • 抽象套接字:通过在sun_path字段的第一个字节设置为null字节(\00x00)来标识。抽象套接字在文件系统中没有对应的条目,因此不会受到文件权限的限制。它们仅存在于内核中,并通过这个null字节前缀进行区分。

为什么要设置p_addr->sun_path[0] = 0x00;

当你设置p_addr->sun_path[0] = 0x00;时,你实际上是在指示这个UNIX域套接字应该作为一个抽象套接字来处理。这样做有几个好处:

  1. 避免文件系统污染:抽象套接字不会在文件系统中留下任何痕迹,因此不会污染文件系统命名空间。
  2. 简化权限管理:由于抽象套接字不依赖于文件系统,因此不需要考虑文件权限问题。
  3. 名称空间隔离:由于抽象套接字的名称仅在内核中有效,因此它们提供了更好的名称空间隔离,减少了不同应用程序之间名称冲突的可能性。

注意事项

  • 抽象套接字的名称长度限制可能因系统而异,但通常较短(例如,Linux中通常是108字节,包括null终止符)。
  • 由于抽象套接字名称不在文件系统中,因此无法通过文件系统操作(如lsrm等)来查看或删除它们。它们仅在内核中有效,并在套接字关闭时自动销毁。
  • 抽象套接字的名称在内核中是唯一的,但仅限于当前运行的进程。当进程终止时,其创建的抽象套接字名称将不再可用。

代码:

#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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值