UNIX域套接字

参考:《UNIX 网络编程 · 卷1 : 套接字联网API》

概述

Unix域套接字不是一个协议族,是单个主机上进程间通信的一种方法。它提供了两类套接字:字节流套接字和数据报套接字,类似于TCP和UDP套接字。

虽然socket套接字可用于同一目的,但UNIX域套接字的效率更高。UNIX域套接字仅仅复制数据,它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。

Unix域套接字提供流和数据报两种接口。UNIX域数据报服务是可靠的,既不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。

Unix域中用于标识客户端和服务器协议地址是普通文件系统的路径名。但这些路径名不是普通的Uinx文件,除非把他们和Unix套接字关联起来,否则无法读写这些文件。

Unix 域套接字地址结构

在头文件<sys/un.h>中定义的Unix域套接字地址结构如下:

struct sockaddr_un
{
    sa_family_t sun_family; //AF_LOCAL
    char sun_path[104];		//null-terminated pathname
}

sun_path中的路径名必须以\0结尾。

如下简单的示例:

int main(int argc, char **argv)
{
    int sockfd;
    socklen_t len;
    struct sockaddr_un addr1, addr2;
    if (argc != 2)
    {
        printf("argc not is 2.");
        return 0;
    }
    sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    unlink(argv[1]);
    bzero(&addr1, sizeof(addr1));
    addr1.sun_family = AF_LOCAL;
    strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path) - 1);
    bind(sockfd, (struct sockaddr *)&addr1, SUN_LEN(&addr1));
    len = sizeof(addr2);
    getsockname(sockfd, (struct sockaddr *)&addr2, &len);
    printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
    return 0;
}

调用bind捆绑到套接字上的路径就是命令行参数,如果已存在bind就会失败,所以先使用unlink删除这个路径名。

运行结果:

crazyang@zt-2013412:~/UNP$ ./sockaddr /tmp/moose
bound name = /tmp/moose, returned len = 13
    
crazyang@zt-2013412:~/UNP$ ls -l /tmp/moose 
srwxrwxrwx 1 crazyang crazyang 0 Jan  1 22:09 /tmp/moose #显示为s的套接字

socketpair 函数

为了创建一对非命名的、相互连接的UNIX域套接字,用户可以使用它们面向网络的域套接字接口,也可使用socketpair函数。

#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sockfd[2]);
//返回值:若成功则返回0,出错则返回-1

参数:

family:必须为AF_INET。

protocol:必须为0。

type:既可以是SOCK_STREAM,也可以是SOCK_DGRAM。

sockfd:新建的两个套接字描述符作为sockfd[0]和sockfd[1]返回。

本函数类似于Unix的pipe函数,返回两个彼此连接的描述符。当type为SOCK_STREAM时调用socketpair得到的结果成为流管道。与调用pipe创建的普通Unix管道类似,但是流管道是全双工的,即两个描述符都是可读可写的。

基本用法:

  1. 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读。

  2. 如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功。

  3. 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。

示例程序:

#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <error.h> 
#include <errno.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
 
const char* str = "SOCKET PAIR TEST.";
 
int main(int argc, char* argv[]){
    char buf[128] = {0};
    int socket_pair[2]; 
    pid_t pid; 
 
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) { 
        printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
        return EXIT_FAILURE; 
    } 
 
    pid = fork();
    if(pid < 0) {
        printf("Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
        return EXIT_FAILURE;
    } else if(pid > 0) {
        //关闭另外一个套接字
        close(socket_pair[1]);
        int size = write(socket_pair[0], str, strlen(str));
        printf("Write success, pid: %d\n", getpid());
 
    } else if(pid == 0) {
        //关闭另外一个套接字
        close(socket_pair[0]);
        read(socket_pair[1], buf, sizeof(buf));        
        printf("Read result: %s, pid: %d\n",buf, getpid());
    }
 
    for(; ;) {
        sleep(1);
    }
 
    return EXIT_SUCCESS;    
}

Unix 域套接字的差异

  1. bind创建的路径名默认访问权限为0777。
  2. 与Unix套接字关联的应该是一个绝对路径名而不是一个相对路径名。
  3. connect调用中指定的路径名必须是一个当前绑定在某个打开的Unix域套接字上的路径名,而且它们的套接字类型必须一致。出错条件包括:1.该路径名已存在却不是一个套接字。2.该路径名已存在且是一个套接字但没有与之关联的打开的描述符。3.路径名类型不符合。
  4. 调用connect连接一个Unix域套接字涉及的权限测试等同于调用open以只写的方式访问相应的路径名。
  5. 如果某个域字节流套接字的connect调用发现这个监听套接字队列已满,调用就立即返回一个ECONNERFUSED错误。不同于TCP发起端会数次发送SYN进行重试。
  6. Unix套接字类似于TCP套接字,他们都为进程提供一个无记录边界的字节流接口。
  7. Unix域数据报套接字类似于UDP套接字,它们都提供一个保留记录边界的不可靠的数据报服务。
  8. 在一个未绑定的Unix域套接字上发送数据报不会自动给这个套接字捆绑一个路径名,这一点不同于UDP套接字。对于某个Unix域数据报套接字的connect调用不会给本套接字捆绑一个路径名。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值