赞
踩
unix域套接字用于服务端和客户端均位于同一台主机上进行通讯的场景,相比较通过127.0.0.1回环口的tcp,udp的优势在于效率。unix域套接字的效率提高一倍。所以很有必有了解下unix域套接字。
套接字编程需要额外包含如下一些基本的头文件:
#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))
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则是一个不存在的路径即可。
下面通过一个服务端和客户端的程序来说明其流程。
/*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;
}
/*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;
}
运行服务端后的打印如下:
运行客户端,发送一段数据,服务端将数据回送给客户端,打印如下:
我们再看下服务端和客户端的path的类型,如下图:
可见 /usrdata/chengdu /usrdata/chengdu1 的类型是s,套接字类型。
我们再netstat看下unix套接字的网络状态:
由于客户端已经退出,所以服务端处于监听(listen)状态,后面的path和我们设置的值是吻合的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。