unix域套接字编程

        unix域套接字用于服务端和客户端均位于同一台主机上进行通讯的场景,相比较通过127.0.0.1回环口的tcp,udp的优势在于效率。unix域套接字的效率提高一倍。所以很有必有了解下unix域套接字。

1.套接字基本头文件

套接字编程需要额外包含如下一些基本的头文件:

#include <sys/socket.h>

头文件包含:socket,socketpair,getsockname,connect,getpeername,send,recv,

sendto,recvfrom,sendmsg,recvmsg,getsockopt,setsockopt,listen,accept,shutdown等API。

#include <netinet/in.h>
头文件包含:如下常用的宏定义以及结构定义
typedef uint32_t in_addr_t;
#define INADDR_ANY     ((in_addr_t) 0x00000000)
#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
struct in_addr
struct in6_addr
struct sockaddr_in
struct sockaddr_in6

#include <arpa/inet.h>
头文件包含:如下常用见地址转换接口
in_addr_t inet_addr (const char *__cp) 
int inet_aton (const char *__cp, struct in_addr *__inp)
char *inet_ntoa (struct in_addr __in)

int inet_pton (int __af, const char *__restrict __cp,void *__restrict __buf) 
const char *inet_ntop (int __af, const void *__restrict __cp,char *__restrict __buf, socklen_t __len)

#include <sys/un.h>
头文件包含:unix域结构
/* Structure describing the address of an AF_LOCAL (aka AF_UNIX) socket.  */
struct sockaddr_un
{
    __SOCKADDR_COMMON (sun_);          /*sun_family*/
    char sun_path[108];        /* Path name.  */
};

#define  __SOCKADDR_COMMON(sa_prefix)      sa_family_t sa_prefix##family
#define  __SOCKADDR_COMMON_SIZE    (sizeof (unsigned short int))
 

2.unix域套接字编程

unix域套接字编程和常规的tcp,udp编程使用的api完全一致,只是使用的套接字结构不一样。

ipv4上使用的是 struct sockaddr_in或者通用结构struct sockaddr,ipv6上使用的是struct sockaddr_in6或者通用结构struct sockaddr_storage,unix域使用的是struct sockaddr_un。struct sockaddr_un定义如下:

struct sockaddr_un
{
    __SOCKADDR_COMMON (sun_);          /*sun_family*/
    char sun_path[108];        /* Path name.  */
};

unix域套接字的domain域,必须为AF_LOCAL(AF_UNIX)

sun_path则是一个不存在的路径即可。

下面通过一个服务端和客户端的程序来说明其流程。

2.1服务端程序

/*unix域字节流套接字*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


/*创建服务端*/
int main(int argc,char *argv[])
{
    char buf[4096];
    size_t size=4096,length=0;
    socklen_t len=0;
    int sockfd=0,client_fd=0;
    struct sockaddr_un server_addr;
    struct sockaddr_un client_addr;

    if(argc != 2){
        printf("usage:cmd path \r\n");
        return -1;
    }

    if((sockfd=socket(AF_LOCAL,SOCK_STREAM,0))<0){
        printf("socket AF_LOCAL error,errno:%d \r\n",errno);
        return -1;
    }

    unlink(argv[1]);
    printf("sun_path:%s \r\n",argv[1]);
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sun_family = AF_LOCAL;
    strcpy(server_addr.sun_path,argv[1]);
    if(bind(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr))<0){
        printf("bind error,errno:%d \r\n",errno);
        return -1;
    }

    if(listen(sockfd,5)<0){
        printf("listen error,errno:%d \r\n",errno);
        return -1;
    }

    for(; ;){
        len = sizeof(struct sockaddr_un);
        if((client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&len))<0){
            printf("accept error,errno:%d \r\n",errno);
            continue;
        }
        printf("accept fd:%d,sun_family:%d,sun_path:%s \r\n",client_fd,client_addr.sun_family,client_addr.sun_path);

        while(1){
            memset(buf,0,size);
            length = recv(client_fd,buf,size,0);
            if(length==0){
                printf("client close the socket \r\n");
                close(client_fd);
                client_fd = 0;
                break;
            }
            printf("server recv content:%s \r\n",buf);
            send(client_fd,buf,length,0);
            usleep(200000);
        }

    }

    return 0;
}

2.2 客户端程序

/*unix域套接字字节流客户端*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
    char buf[4096];
    size_t size=4096;
    int sockfd=0;
    struct sockaddr_un server_adrr;
    struct sockaddr_un client_addr;

    if(argc != 4){
        printf("usage:cmd client_path server_path content\r\n");
        return -1;
    }

    if((sockfd=socket(AF_LOCAL,SOCK_STREAM,0))<0){
        printf("socket AF_LOCAL error,errno:%d \r\n",errno);
        return -1;
    }

    unlink(argv[1]);
    printf("client sun_path:%s \r\n",argv[1]);
    client_addr.sun_family = AF_LOCAL;
    strcpy(client_addr.sun_path,argv[1]);
    if(bind(sockfd,(struct sockaddr *)&client_addr,sizeof(struct sockaddr_un))<0){
        printf("bind error,errno:%d \r\n",errno);
        return -1;
    }

    printf("server sun_path:%s \r\n",argv[2]);
    server_adrr.sun_family = AF_LOCAL;
    strcpy(server_adrr.sun_path,argv[2]);
    if(connect(sockfd,(struct sockaddr *)&server_adrr,sizeof(struct sockaddr_un))<0){
        printf("connect error,errno:%d \r\n",errno);
        return -1;
    }

    printf("send content:%s \r\n",argv[3]);
    memset(buf,0,size);
    send(sockfd,argv[3],strlen(argv[3])+1,0);
    recv(sockfd,buf,size,0);
    printf("client recv content:%s \r\n",buf);

    close(sockfd);

    return 0;
}

2.3 测试

运行服务端后的打印如下:

运行客户端,发送一段数据,服务端将数据回送给客户端,打印如下:

我们再看下服务端和客户端的path的类型,如下图:

可见 /usrdata/chengdu /usrdata/chengdu1 的类型是s,套接字类型。

我们再netstat看下unix套接字的网络状态:

由于客户端已经退出,所以服务端处于监听(listen)状态,后面的path和我们设置的值是吻合的。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值