※
unix域套接字实际上不是一个实际的协议,他只是在同一台主机上客户和服务器之间通信时,使用与在不同主机上客户和服务器间通信时相同的API
unix域套接字分为两种:字节流套接字和数据报套接字
unix域套接字的好处:
1 在同一台主机上进行通信时,是不同主机间通信的两倍
2 unix域套接口可以在同一台主机上,不同进程之间传递套接字描述符
3 unix域套接字可以向服务器提供客户的凭证(用户id或者用户组id)
unix域套接字使用的地址通常是文件系统中一个文件路径(套接口文件:APUE中的4.3节文件类型,是以s开头的),这些文件不是不同的文件,只能作为域套接字通信,不能读写
并且是以s开头的文件
unix域套接字的地址结构是:
struct sockaddr_un
{
uint8_t sun_len;
sa_family_sun_family;//AF_LOCAL
char sun_path[104];//必须是以空结尾的字符串(路径+文件名)
}
int socketpair(int family, int type, intprotocol, int sockfd[2]);
这个函数创建两个互相连接的套接字(socketfd[2])family 是AF_LOCAL, type可以是SOCK_STREAM (字节流)或者SOCK_DGRAM(数据报),协议是0,之后就能够或者两个互相连接的套接字
以SOCK_STREAM调用的socketpair函数得到的套接字叫做流管道(stream pipe),是全双工的,就是这两个套接字是可读可写的
※
1在unix域套接字进行bind的时候建立套接口文件,其默认的权限值是0777,并被当前的umask修改,看上图就知道,umask是0022 的到的文件权限是0755
2关于bind创建文件中地址参数 sockaddr_un 中的sun_path需要是绝对路径,这样才能不用考虑相对的概念。防止客户端程序也用相对路径,但是和服务器不在同一个目录的考虑
3 connect中的地址中的路径名必须是套接口文件,而且已经被服务端绑定的,以下情况会出错:1 文件路径存在但是不是套接口文件2 路径名存在且是套接口文件,但是没有和该文件绑定的套接口3 就是type必须和服务器相同
4 connect连接unix套接口的时候的权限检查和open函数一样的
5 unix域字节流套接口和TCP一样都给进程提供一个无记录边界的字节流接口
6 unix域套接口connect发现等待队列满了,就直接返回ECONNREFUSRD错误,说连接拒绝错误
7 unix域数据报套接口和UDP一样提供一个保留记录边界的不可靠数据报服务
unix域套接字的客户端:
#include <sys/un.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MAX_SEND 1025
#define UNIX_PATH "/tmp/sinfor"
void dump_unix(int sock_fd)
{
char tmp[MAX_SEND] = {0};
char recv[MAX_SEND] = {0};
while(fgets(tmp, MAX_SEND, stdin) != NULL)
{
write(sock_fd, tmp, strlen(tmp));
read(sock_fd, recv, MAX_SEND);
printf("data : %s\n", recv);
bzero(tmp,MAX_SEND);
bzero(recv, MAX_SEND);
}
}
int main(int argc, char** argv)
{
int conn_sock = socket(AF_LOCAL, SOCK_STREAM, 0);
if(conn_sock == -1)
{
perror("socket fail ");
return -1;
}
struct sockaddr_un addr;
bzero(&addr, sizeof(addr));
addr.sun_family = AF_LOCAL;
strcpy((void*)&addr.sun_path, UNIX_PATH);
if(connect(conn_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
perror("connect fail ");
return -1;
}
dump_unix(conn_sock);
close(conn_sock);
return 0;
}
unix域套接字的服务器;
#include <sys/un.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#define MAX_RECV 1025
#define UNIX_SERV_PATH "/tmp/sinfor"
void client_dump(int sock_fd)
{
char rec[MAX_RECV] = {0};
int size;
while((size = read(sock_fd, rec, MAX_RECV)) != 0)
{
printf("**********1111************\n");
printf("recv data is %s\n", rec);
write(sock_fd, rec, size);
}
}
void sig_son(int num)
{
printf("son is %d\n", getpid());
wait(NULL);
//return NULL;
}
int main(int argc, char** argv)
{
int acc_sock, dump_sock;
acc_sock = socket(AF_LOCAL, SOCK_STREAM, 0);
if(acc_sock == -1)
{
perror("socket func fail ");
return -1;
}
struct sockaddr_un ser_addr, cli_addr;
ser_addr.sun_family = AF_LOCAL;
strcpy(ser_addr.sun_path, UNIX_SERV_PATH);
unlink(UNIX_SERV_PATH);
bind(acc_sock, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
listen(acc_sock, 5);
signal(SIGCHLD, sig_son);
while(1)
{
int len = sizeof(cli_addr);
dump_sock = accept(acc_sock, (struct sockaddr*)&cli_addr, &len);
if(dump_sock == -1)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept fail");
return -1;
}
}
if(fork() == 0)
{
close(acc_sock);
client_dump(dump_sock);
close(dump_sock);
exit(0);
}
close(dump_sock);
}
close(acc_sock);
return 0;
}