Unix域协议并不是一个实际的协议族,它只是在同一台主机上进行客户-服务器通信时,使用与在不同主机上的客户和服务器间通信时相同的API的一种方法。

当客户和服务器在同一台主机上时,Unix域协议是这套系列书的第二卷将介绍的IPC通信方式的一种替代品。

Unix域提供了两种类型的套接口:字节流套接口(与TCP类似)、数据报套接口(与UDP类似)

使用Unix域套接口有三个原因:

  1. 当通信双方位于同一台主机上时,Unix域套接口的速度通常是TCP套接口的两倍。
  2. Unix域套接口可以用来在同一台主机上的各进程之间传递描述字。
  3. Unix域套接口的较新实现中可以向服务器提供客户的凭证,这能提供附加的安全检查。

二、Unix域套接口地址结构

struct sockaddr_un{
uint8_t sun_len;
sa_family_t sun_family; /* AF_LOCAL */
char sun_path[]; /* null-terminated pathname */
};

三、socketpair函数

socketpair函数建立一对相互连接的套接口,这个函数只对Unix域套接口适用

#include <sys/socket.h>

int socketpair(int family, int type, int protocol, int sockfd[]);
返回:成功0,出错-

family:必须为AF_LOCAL

protocol:必须为0

type:可以是SOCK_STREAM或SOCK_DGRAM

新创建的两个套接口描述字作为sockfd[0]和sockfd[1]返回

socketpair的多进程例子

 #include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h> #define SOCKET_BUFFER_SIZE (32768U) void *thread_function(void *arg)
{
int len = ;
int fd = *((int *)(arg));
char buf[];
int cnt = ; while() {
len = sprintf(buf, "Hi, main process, cnt = %d", cnt++);
write(fd, buf, len); len = read(fd, buf, );
buf[len] = '\0';
printf("%s\n", buf); sleep();
} return NULL;
} int main()
{
int ret;
int sockets[];
int bufferSize = SOCKET_BUFFER_SIZE;
pthread_t thread; ret = socketpair(AF_UNIX, SOCK_SEQPACKET, , sockets);
if(ret == -) {
printf("socketpair create error!\n");
return -;
} setsockopt(sockets[], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); pthread_create(&thread, NULL, thread_function, (void *)(&sockets[])); int len = ;
int fd = sockets[];
char buf[];
int cnt = ; while() {
len = read(fd, buf, );
buf[len] = '\0';
printf("%s\n", buf); len = sprintf(buf, "Hi, thread process, cnt = %d", cnt++);
write(fd, buf, len);
}
return ;
}

socketpair

四、套接口函数

当用于Unix套接口时,套接口函数有一些差别和限制。需注意的是并不是所有的实现中都做到了这一级。

  1. bind建立的路径名的缺省访问权限应为0777,并被当前的umask值修改。
  2. 与Unix域套接口相关联的路径名应为一个绝对路径名,而不是相对路径名。避免使用后者的原因是它依赖于调用者的当前工作目录。
  3. connect使用的路径名必须是一个绑定的某个已打开的Unix域套接口上的路径名,而且套接口的类型也必须一致。
  4. 用connect连接Unix域套接口时的权限检查和用open以只写方式访问路径名时完全相同。
  5. Unix域字节流套接口和TCP套接口类似:它们都为进程提供一个没有记录边界的字节流接口
  6. 如果Unix域字节流套接口的connect调用发现监听套接口的队列已满,会立刻返回一个ECONNREFUSED错误。如果监听套接口的队列已满,它将忽略到来的SYN,TCP连接的发起方会接着发送几次SYN重试。
  7. Unix域数据报套接口和UDP套接口类似,他们都提供一个保留记录边界的不可靠的数据报服务。
  8. 与UDP套接口不同的是,在未绑定的Unix域套接口上发送数据报不会给它绑定一个路径名。

五、描述字传递

从考虑从一个进程向另一个进程传递打开的描述字时,我们通常会想到:

  1. 在fork调用后,子进程共享父进程的所有打开的描述字
  2. 在调用exec时所有描述字仍保持打开

第一个例子中进程打开一个描述字,调用fork,然后父进程关闭描述字,让子进程处理这个描述字。

这样将一个打开的描述字从父进程传递到子进程。但我们也想让子进程打开一个描述字并捡起传递给父进程。

  1. 创建一个字节流的或数据报的Unix域套接口。
  2. 进程可以用任何返回描述字的Unix函数打开一个描述字。
  3. 发送进程建立一个msghdr接口,其中包含要传递的描述字
  4. 接收进程调用recvmsg在Unix域套接口上接收描述字。

两个进程用socketpair通信文件描述符,并对打开的文件写入的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream> using namespace std; #include <sys/socket.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/uio.h> void send_fd(int sock, int sendfd)
{
struct msghdr msg;
struct iovec iov[];
char buf[];
int cmsgsize = CMSG_LEN(sizeof(int));
struct cmsghdr *cmptr; cmptr = (cmsghdr *)malloc(cmsgsize);
if(cmptr == NULL) {
cout<<"[send_fd] init cmptr error"<<endl;
exit();
} cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS; /* send file description */
cmptr->cmsg_len = cmsgsize;
*((int *)CMSG_DATA(cmptr)) = sendfd; iov[].iov_base = buf;
iov[].iov_len = sizeof(buf); msg.msg_name = NULL;
msg.msg_namelen = ;
msg.msg_iov = iov;
msg.msg_iovlen = ;
msg.msg_control = cmptr;
msg.msg_controllen = cmsgsize; int ret = sendmsg(sock, &msg, );
free(cmptr);
if(ret == -) {
cout<<"[send_fd] sendmsg error"<<endl;
exit();
}
} int recv_fd(int sock)
{
struct msghdr msg;
struct iovec iov[];
char buf[];
int cmsgsize = CMSG_LEN(sizeof(int));
struct cmsghdr *cmptr; iov[].iov_base = buf;
iov[].iov_len = sizeof(buf); // cmptr = CMSG_FIRSTHDR(&msg);搞不明白为什么用这个函数不行
cmptr = (cmsghdr *)malloc(cmsgsize);
if(cmptr == NULL) {
cout<<"[send_fd] init cmptr error"<<endl;
exit();
} msg.msg_name = NULL;
msg.msg_namelen = ;
msg.msg_iov = iov;
msg.msg_iovlen = ;
msg.msg_control = cmptr;
msg.msg_controllen = cmsgsize; int ret = recvmsg(sock, &msg, );
free(cmptr);
if(ret == -) {
cout<<"[recv_fd] recvmsg error"<<endl;
exit();
} int fd = *((int *)CMSG_DATA(cmptr));
return fd;
} void worker_process_cycle(int sockets[])
{
cout<<"worker process #"<<getpid()<<endl;
int fd = sockets[];
int file = recv_fd(fd);
if(file < ) {
cout<<"[worker] invalid fd!"<<endl;
exit();
}
char msg[] = "child process";
cout<<"[worker] write file #"<<file<<" ret = "
<<write(file, msg, sizeof(msg))<<endl; close(file);
exit();
} void master_process_cycle(int sockets[])
{
cout<<"master process #"<<getpid()<<endl;
int fd = sockets[]; system("rm -f ./newfile");
int file = open("./newfile", O_CREAT|O_TRUNC|O_RDWR);
cout<<"[master] dispath fd to worker process, file=#"<<file<<endl;
send_fd(fd, file); int status;
waitpid(-, &status, );
exit();
} int main(int argc, char *argv[])
{
cout<<"current pid: "<<getpid()<<endl;
int sockets[];
if(socketpair(AF_UNIX, SOCK_STREAM, , sockets) == -) {
cout<<"failed to create domain socket by socketpair"<<endl;
exit();
}
cout<<"create domain socket by sockpair success"<<endl; cout<<"create process to communicate over domain socket"<<endl;
pid_t pid = fork();
if(pid == ) {
worker_process_cycle(sockets);
} else {
master_process_cycle(sockets);
} for( ; ; ) {
pause();
}
}

UNP学习 Unix域协议的更多相关文章

  1. UNP学习笔记(第十五章 UNIX域协议)

    UNIX域协议是在单个主机上执行客户/服务器通信的一种方法 使用UNIX域套接字有以下3个理由: 1.UNIX域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍 2.UNIX域套接字可用于在同 ...

  2. 《Unix 网络编程》15:Unix 域协议

    Unix 域协议 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ 本 ...

  3. 《TCP-IP详解卷3:TCP 事务协议、HTTP、NNTP和UNIX域协议》【PDF】下载

    TCP-IP详解卷3:TCP 事务协议.HTTP.NNTP和UNIX域协议>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062539 ...

  4. UNIX域协议(命名套接字)

    这里主要介绍命名UNIX域套接字 1.什么是UNIX域套接字Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务通信的一种方式.是进程间通信(IPC)的一种方式.它提供了两类套接字:字 ...

  5. Socket编程实践(13) --UNIX域协议

    UNIX域协议 UNIX域套接字与TCP相比, 在同一台主机上, UNIX域套接字更有效率, 几乎是TCP的两倍(由于UNIX域套接字不需要经过网络协议栈,不需要打包/拆包,计算校验和,维护序号和应答 ...

  6. UNIX网络编程读书笔记:UNIX域协议

    概述 UNIX域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法,所用API与在不同主机上执行客户/服务器通信所用的API(套接口API)相同.UNIX域协议可视为进程间通信 ...

  7. UNIX域协议(无名套接字)

    关于什么是UNIX域套接字可以参考:http://www.cnblogs.com/xcywt/p/8185597.html这里主要介绍非命名的UNIX域套接字的用法.1.socketpair函数先看m ...

  8. python3实现unix域协议(tcp、udp)通讯

    socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIXDomain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通过loopb ...

  9. UNIX域协议之描述符传递

    一.mycat程序 #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #define BUFFS ...

随机推荐

  1. 【LeetCode 57】插入区间

    题目链接 [题解] 这题要分四种情况. 第一种.区间在所有区间的前面. 第二种.区间在所有区间的后面. 第三种.区间在某两个区间之间但是没有交集. 第四种.区间和某个区间产生了相交. 对于第四种枚举第 ...

  2. 网络协议之mDNS

    DNS(Domain Name System,域名系统)因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串.通过主机名,最终得 ...

  3. HihoCoder 1055 刷油漆 (树上背包)

    题目:https://vjudge.net/contest/323605#problem/A 题意:一棵树,让你选择m个点的一个连通块,使得得到的权值最大 思路:树上背包,我们用一个dp数组,dp[i ...

  4. POJ - 1655 (点分治-树的重心)

    题目:https://vjudge.net/contest/307753#problem/D 题意:给你一棵树,让你求出一个点,让他的最大子树的节点数尽量小 思路:最大子树节点数尽量小,一看就是树的重 ...

  5. 2018-2019-2 《Java程序设计》第10周学习总结

    20175319 2018-2019-2 <Java程序设计>第10周学习总结 教材学习内容总结 本周学习<Java程序设计>第十二章java多线程机制: - 进程与线程 程序 ...

  6. 基于Springmvc的登录权限拦截器

    1.什么是拦截器 拦截器是指通过统一拦截从浏览器发往服务端的请求来完成功能的增强. 使用场景:解决请求的共性问题(如:乱码问题,权限验证问题等) 2.拦截器的基本工作原理 springmvc可以通过配 ...

  7. linux设备驱动学习笔记--内核调试方法之printk

    1,printk类似于用户态的printf函数,但是比printf函数多了一个日志级别,内核中最常见的日志输出都是通过调用printk来实现的,其打印级别有8种可能的记录字串, 在头文件 <Li ...

  8. nginx配置-location

    以 =开头表示精确匹配如 A 中只匹配根目录结尾的请求,后面不能带任何字符串. ^~ 开头表示uri以某个常规字符串开头,不是正则匹配 ~ 开头表示区分大小写的正则匹配; ~* 开头表示不区分大小写的 ...

  9. python导入自定义模块和包

    参考资料 https://blog.csdn.net/gvfdbdf/article/details/52084144 http://www.runoob.com/python/python-modu ...

  10. Java并发:搞定线程池(中)

    向线程池提交任务 1.1 execute()     用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功.输入的是一个Runnable实例. public void execute(Ru ...