UNIX域套接字编程

简介

UNIX 域套接字(UDS):UNIX Domain socket。

Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通讯的一种方式,单个主机上执行通信,也就是所谓的进行间通信(IPC),所以Unix域套接字协议可以视作IPC方法之一。

虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率,因为UDS不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。

IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

Unix域提供两中套接字API:字节流套接字(类似TCP)和数据报套接字(类似DUP)。但是面向数据包的UDS也是可靠的,消息既不会丢失也不会顺序错乱。

Unix域套接字地址结构

使用UDS的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径(普通文件系统中的路径名),这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

#define UNIX_PATH_MAX    108

#include <sys/un.h>
struct sockaddr_un {
    sa_family_t sum_family;// AF_LOCAL或者AF_UNIX
    char sun_path[104];//字符串指代路径(null终止)
};

sun_path表示与套接字关联的地址,以NULL结尾,如果未指定地址通则通过以空字符串作为路径名指示,也就是说sun_path[0]值为0,这个效果就好像Ipv4的INADDR_ANY和IPv6的ADDR_ANY_INIT

 

注意:UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN。

UDS编程

 

 

socketpair函数

这个是UNIX域套接字特有的函数,它创建两个连接起来的套接字。

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

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

family: 为AF_LOCAL或者AF_UNIX。 
type:既可以是SOCK_STREAM也可以是SOCK_DGRAM。 
protocol:必须为0。 
sockfd[2]:新创建的两个套接字别在sockfd[0]和sockfd[1]中返回; sockfd[2]可用于同进程通信也可用于不同进程间通信

socketpair 编程

 

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

#define ON 0

void parent_process(int fd)
{
    char buff[100] = {0};
    while(1)
    {
        memset(buff,0,sizeof(buff));
        read(fd,buff,50);
        printf("parent,%s\n",buff);
        memset(buff,0,sizeof(buff));
        strcpy(buff,"hello,I am parent");
        write(fd,buff,strlen(buff));
        sleep(1);
    }
}

void child_process(int fd)
{
    char msg[100] = {0};
    while(1)
    {
        memset(msg,0,sizeof(msg));
        strcpy(msg,"hello,I am child");
        write(fd,msg,strlen(msg));
        read(fd,msg,100);
        printf("child,%s\n",msg);
        sleep(1);
    }
}

void one_process_exchange(int wfd,int rfd)
{
    char msg[100] = {0};
    strcpy(msg,"一个进程通信");
    write(wfd,msg,strlen(msg));
    memset(msg,0,sizeof(msg));
    read(rfd,msg,100);
    printf("%s",msg);
}

int main()
{
    int srv[2];
    int ret = 0;
    if( socketpair( AF_UNIX,SOCK_DGRAM,0,srv)<0 )
    {
        perror("socketpair:");
        return 0;
    }
#if ON
    one_process_exchange(srv[0],srv[1]);//测试同进程间通信

#else
    pid_t chldpid = fork();  //测试不同进程间通信
    if ( chldpid == 0)
    {
        close(srv[1]);
        child_process( srv[0] );
    }
    else if ( chldpid > 0 )
    {
        close(srv[0]);
        parent_process( srv[1] );
    }
    else
    {
        perror("fork");
    }
#endif
}

同进程间通信结果:

root@liuxinju-VirtualBox:/home/liuxinju/work/linuxapi# ./a.out 
一个进程通信

不同进程间通信:

root@liuxinju-VirtualBox:/home/liuxinju/work/linuxapi# ./a.out 
parent,hello,I am child
child,hello,I am parent
parent,hello,I am child
child,hello,I am parent
parent,hello,I am child
child,hello,I am parent
^C

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值