Unix域协议简介

Unix Domain Socket(UDS)提供了在本地进程间高效通信的方式,包括字节流和数据报模式,支持描述符传递和客户凭证。与本地环回TCP/UDP相比,UDS在某些场景下性能更优,如在发送大量数据时。文章还探讨了UDS的性能评估、缓冲区管理和应用策略。
摘要由CSDN通过智能技术生成

前言

已经不记得什么时候第一次接触Unix Domain Socket(下文简称UDS),在我印象中,所谓UDS基本等同于本地环回接口(lo)上的TCP或者UDP,而事实上UDS所用的API也确实是套接字API。也许正因为这些先入为主的观点,自从进入后台开发这个领域,基本没有正眼瞧过UDS,觉得不过是单机版的TCP/UDP而已。

最近shane兄的《OIDBset实践和技术方案》在KM上引发了激烈的讨论,当时不少人提到可以借助eventfd执行事件通知,事后我想实际做一些性能评测时,却遇到一个比较棘手的问题:eventfd创建出来的“文件描述符”在无父子关系的多进程环境中该如何共享呢?大家熟知的pipe有所谓的“命名管道”,可eventfd没有这概念。后来jayyi提到可以借助Unix域协议传递文件描述符,从那一刻起,我才决定好好研究一下这个“最熟悉的陌生人”。

概述

UDS提供两类套接字:字节流套接字(类似TCP)和数据报套接字(类似UDP)。根据《UNIX网络编程卷1》描述,使用UDS有以下3个理由:

1.         在源自Berkeley的实现中,Unix域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍

2.         Unix域套接字可用于在同一个主机上的不同进程之间传递描述符

3.         Unix域套接字较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施

字节流模式

/*
* unix_stream_server.c
*/

#define _GNU_SOURCE //for struct ucred

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

#define SERVER_PATH "/tmp/stream_server.unix"

static int ConnHandler(int fd)
{
    struct ucred credentials;
    socklen_t ucred_length = sizeof(credentials);
    if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length) < 0) {
        perror("getsockopt fail.");
        return -1;
    }
    printf("[CONN]pid=%d uid=%d gid=%d\n", (int)credentials.pid, (int)credentials.uid, (int)credentials.gid);

    char buffer[65535];
    uint64_t total = 0;
    int readn = 0;
    while ((readn = read(fd, buffer, sizeof(buffer))) > 0) {
        total += readn;
    }
    if (readn == 0) {
        printf("client close conn.\n");
    } else {
        perror("read fail.");
    }
    printf("total=%lu\n", total);
    close(fd);
    return 0;
}

int main(void)
{
    unlink(SERVER_PATH);

    int fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if (fd < 0) {
        perror("open socket fail.");
        return -1;
    } 

    struct sockaddr_un local_addr;
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sun_family = AF_UNIX;
    snprintf(local_addr.sun_path, sizeof(local_addr.sun_path), SERVER_PATH);
    if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
        perror("bind fail.");
        return -1;
    }
    if (listen(fd, 0) < 0) {
        perror("listen fail.");
        return -1;
    }

    while (1) {
        struct sockaddr_un client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int conn_fd = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len);
        if (conn_fd < 0) {
            perror("accept fail.");
            return -1;
        }
        pid_t child = fork();
        if (child == 0) {
            return ConnHandler(conn_fd);
        }
        close(conn_fd);
    }

    return 0;
} 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值