IPC of UNIX Domain Socket基础程序实现

x86/debian gnu linux/gcc


1. 概念

UNIX Domain Socketsocket的框架上发展出一种IPC机制socket API原本是为网络通讯设计的


UNIX Domain Socket全双工,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制


使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选SOCK_DGRAMSOCK_STREAM,protocol参数仍然指定为0即可。UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un,网络编程的”socket地址”是IP地址加端口号,而”UNIX Domain Socket的地址”是一个socket类型的文件在文件系统中的路径(isan one of  ipc),这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,bind()错误返回。


2 IPC -- UNIX Domian socket code

(1) server.c(just one name of a process)

/*Filename:     uds_ipc_server.c
 *Brife:        Bind UNIX DOMAIN SOCKET's fd to socket address as server
 *              When client connect, IPC will come true
 *Author:       One fish
 *Date:         2014.9.10 Wed
 */
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>

#define FILE_PATH       "./UDS_ser_file.socket"
#define QLEN            10


int bind_unix_dmn_socket(const char *filename);
int serv_accept(int listenfd, uid_t *uidptr);


int main(void)
{
        int     listenfd;
        uid_t   uidptr;

        listenfd        = bind_unix_dmn_socket(FILE_PATH);
        if (listenfd > 0) {
                serv_accept(listenfd, &uidptr);
        }

        return 0;
}

//Bind UNIX Domain socket to an address
int bind_unix_dmn_socket(const char *filename)
{
        int                     fd, size;
        struct sockaddr_un      un;

        memset(&un, 0, sizeof(un));
        un.sun_family   = AF_UNIX;
        strcpy(un.sun_path, filename);

        //Create one UNIX Domain socket
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
                perror("socket error");
                return -1;
        }

        size    = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

        unlink(FILE_PATH);
        //Bind UNIX Domain socket to filename by fd
        if (bind(fd, (struct sockaddr *)&un, size) < 0) {
                perror("bind error");
                close(fd);
                return -2;
        }
        printf("UNIX Domain socket(%d) bind to\" %s\" success\n", fd, filename);
        //Tell kernel we're a server
        if (listen(fd, QLEN) < 0) {
                fputs("Listen failed", stderr);
                close(fd);
                return -3;
        }
        return fd;
}

//Accept a client connect
int serv_accept(int listenfd, uid_t *uidptr)
{
        int                     clifd, len, err, rval;
        struct  sockaddr_un     un;
        struct  stat            statbuf;

        len     = sizeof(un);

        //Accept client connect to
        if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0 ) {
                return -1;
        }

        len     -= offsetof(struct sockaddr_un, sun_path);
        un.sun_path[len]        = 0;

        if (stat(un.sun_path, &statbuf) < 0) {
                close(clifd);
                return -2;
        }

        if (S_ISSOCK(statbuf.st_mode) == 0) {
                close(clifd);
                return -3;
        }

        if (uidptr != NULL) {
                *uidptr = statbuf.st_uid;
        }
        printf("client %s which uid is %d connect to this server\n", un.sun_path, *uidptr);
        char str[]= "Wellcome to UNIX Domain socket of IPC";
        send(clifd, str, strlen(str), 0);
        unlink(un.sun_path);

        return clifd;
}

与网络socket编程类似,bind之后要listen,表示通过bind的地址(也就是socket文件)提供服务。


通过accept得到客户端地址也应该是一个socket文件,如果不socket文件就返回错误码,如果是socket文件,在建立连接后这个文件就没有用了,unlink把它删掉,通过传出参数uidptr返回客户端程序的user id


(2) client.c(just one process's name)

/*Filename:     uds_ipc_client.c
 *Brife:        Bind UNIX Domain socket's fd to socket address as client.
 *              Then connect server via server's socket address
 *Author:       One fish
 *Date:         2014.9.10 Wed
 */
#include <stdio.h>
#include <stddef.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>


#define CLI_PATH        "/var/tmp/"
#define SER_FILE_PATH   "./UDS_ser_file.socket"

int cli_conn(const char *filename);


int main(void)
{
        cli_conn(SER_FILE_PATH);
        return 0;
}

//Create a client endpoint and connect to a server.
int cli_conn(const char *filename)
{
        int                     fd, len, err, rval;
        struct  sockaddr_un     un;

        //Create a UNIX domain stream socket
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
                return -1;
        }

        //Fill client's socket address
        memset(&un, 0, sizeof(un));
        un.sun_family   = AF_UNIX;
        sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid());
        len     = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
        unlink(un.sun_path);

        //Bind client's socket fd to its socket address
        if (bind(fd, (struct sockaddr *)&un, len) < 0) {
                close(fd);
                return -2;
        }

        //Fill socket address structure with server's address
        memset(&un, 0, sizeof(un));
        un.sun_family   = AF_UNIX;
        strcpy(un.sun_path, filename);
        len     = offsetof(struct sockaddr_un, sun_path) + strlen(filename);

        //Connect to server
        if (connect(fd, (struct sockaddr *)&un, len) < 0) {
                close(fd);
                return -4;
        }
        char rvbuf[80] = "Server no say";
        recv(fd, rvbuf, 78, 0);
        printf("Server's words:%s\n", rvbuf);
        return fd;
}

与网络socket编程不同的是,UNIX Domain Socket客户端一般要显式调用bind函数,而不依赖系统自动分配的地址。客户端bind一个自己指定的socket文件名的好处,该文件名可以包含客户端的pid以便服务器区分不同的客户端。


compile and execute

in one terminal:

lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$gcc uds_ipc_server.c -o uds_ipc_server

lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$./uds_ipc_server

UNIX Domain socket(3) bind to" ./UDS_ser_file.socket"success



Now in another terminal:

lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$gcc uds_ipc_client.c -o uds_ipc_client

lly@debian:~/mydir/lly_books/linux_c_programming_osl/socket$./uds_ipc_client


The result:

uds_ipc_server:



uds_ipc_client:

[2014.9.10 - 16:56]
LCNote Over.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值