UNIX_SOCKET 进程间通信

使用socket实现进程间通信:(UNIX domain中面向连接通信)

    使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。
man unix内容如下:
NAME( 名称)
    unix, PF_UNIX, AF_UNIX, PF_LOCAL, AF_LOCAL ? 用于本地内部进程通讯的套接 字。
SYNOPSIS( 总览 )
    #include <sys/socket.h>
    #include <sys/un.h>

    unix_socket = socket(PF_UNIX, type, 0);
    error = socketpair(PF_UNIX, type, 0, int *sv);
DESCRIPTION( 描述 )
    PF_UNIX (也称作 PF_LOCAL ) 套接字族用来在同一机器上的提供有效的进程间通讯.Unix 套接字可以是匿名的(由 socketpair(2)创建), 也可以与套接字类型文件相关联. Linux 还支持一种抽象名字空间, 它是独立于文件系统的.
    有效的类型有: SOCK_STREAM 用于面向流的套接字, SOCK_DGRAM 用于面向数据报的套接字,其可以保存消息界限. Unix 套接字总是可靠的,而且不会重组数据报.
    Unix 套接字支持把文件描述符或者进程的信用证明作为数据报的辅助数据传递给 其它进程.
ADDRESS FORMAT( 地址格式 )
    unix 地址定义为文件系统中的一个文件名或者抽象名字空间中的一个单独的字符串. 由 socketpair(2) 创建的套接字是匿名的.对于非匿名的套接字,目标地址 可使用 connect(2) 设置. 本地地址可使用 bind(2) 设置. 当套接字连接上而且它没有一个本地地址时, 会自动在抽象名字空间中生成一个唯一的地址.
    #define UNIX_PATH_MAX   108

    struct sockaddr_un {
    sa_family_t     sun_family;     /* AF_UNIX */
    char    sun_path[UNIX_PATH_MAX];        /* 路径名 */
    };
    sun_family 总是包含 AF_UNIX. sun_path 包含空零结尾的套接字在文件系统中的路径名. 如果 sun_path 以空零字节开头,它指向由 Unix 协议模块维护的抽象名字空间. 该套接字在此名字空间中的地址由 sun_path 中的剩余字节给定. 注意抽象名字空间的名字都不是空零终止的.
SOCKET OPTIONS( 套接字选项 )
    由 于 历 史 原 因, 这些套接字选项通过 SOL_SOCKET 类型确定, 即使它们是 PF_UNIX 指定的. 它们可以由 setsockopt(2) 设置. 通过指定 SOL_SOCKET 作 为套接字族用 getsockopt(2) 来读取.
    SO_PASSCRED 允许接收进程辅助信息发送的信用证明. 当设置了该选项且套接字 尚未连接时, 则会自动生成一个抽象名字空间的唯一名字. 值为一个整数布尔标 识.
ANCILLARY MESSAGES( 辅助信息 )
    由 于 历 史 原 因, 这些辅助信息类型通过 SOL_SOCKET 类型确定, 即使它们是 PF_UNIX 指定的. 要发送它们, 可设置结构 cmsghdr 的 cmsg_level 字 段 为 SOL_SOCKET, 并 设 置 cmsg_type 字段为其类型. 要获得更多信息, 请参看 cmsg(3).
    SCM_RIGHTS
    为其他进程发送或接收一套打开文件描述符. 其数据部分包含一个文件 描述符的整型数组. 已传文件描述符的效果就如它们已由 dup(2) 创建 过一样.
    SCM_CREDENTIALS
    发送或者接收 unix 信用证明. 可用作认证.信用证明传送 以 struct ucred 辅助信息的形式传送.
    struct ucred {
    pid_t   pid;     /* 发送进程的进程标识 */
    uid_t   uid;     /* 发送进程的用户标识 */
    gid_t   gid;     /* 发送进程的组标识 */
    };
    发 送者确定的信用证明由内核检查. 一个带有有效用户标识 0 的进程允许指定 不与其自身值相匹配的值.发送者必须确定其自身的进程 标 识( 除非它带 有 CAP_SYS_ADMIN), 其 用 户 标识,有效用户标识或者设置用户标识(除非它带有CAP_SETUID), 以及其组标识,有效组标识或者 设 置 组 标 识( 除非它带有CAP_SETGID). 为 了 接 收 一 条 struct ucred 消息,必须在套接字上激活 SO_PASSCRED 选项.
ERRORS( 错误 )
    ENOMEM
        内存溢出.
    
    ECONNREFUSED
        connect(2) 调用了一个未在监听的套接字对象. 这可能发生在远程套 接字不存在或者文件名不是套接字的时候.
    EINVAL
        传递了无效参数. 通常的产生原因是已传地址的 sun_type 字 段的 AF_UNIX 设置丢失, 或者套接字对应用的操作处于无效状态.
    EOPNOTSUPP
        在非面向流的套接字上调用了流操作,或者试图使用出界的数据选项.
    EPROTONOSUPPORT
        传递的协议是非 PF_UNIX 的.
    ESOCKTNOSUPPORT
        未知的套接字类型.
    EPROTOTYPE
        远程套接字与本地套接字类型不匹配 (SOCK_DGRAM 对SOCK_STREAM).
    EADDRINUSE
        选择的本地地址已经占用,或者文件系统套接字对象已经存在.
    EISCONN
        在 一个已经连接的套接字上调用 connect(2) 或者指定的目标地址在一 个已连接的套接字上.
    ENOTCONN
        套接字操作需要一个目的地址,但是套接字尚未连接.
    ECONNRESET
        远程套接字意外关闭.
    EPIPE
        远程套接字在一个流套接字上关闭了.如果激活,会同时发送一个 SIGPIPE 标识.这可以通过传递 MSG_NOSIGNAL 标识给 sendmsg(2) 或者 recvmsg(2) 来避免.
    EFAULT
        用户内存地址无效.
    EPERM
        发送者在 struct ucred 中传递无效的信用证明.
    当生成一个文件系统套接字对象时, 可能会由通用套接层或者文件系统产生其它错误. 要获得更多信息,可参见合适的手册页.

 

实践:

    使用套接字在UNIX域内实现进程间通信的服务端程序。首先,程序通过调用socket函数,建立了监听连接的套接字,然后调用bind函数,将套接字与地址信息关联起来。调用listen函数实现对该端口的监听,当有连接请求时,通过调用accept函数建立与客户机的连接,最后,调用read函数来读取客户机发送过来的消息,当然也可以使用recv函数实现相同的功能。

server代码:s_unix.c

 

[c-sharp]  view plain copy
  1. //s_unix.c  
  2. #include <stdio.h>  
  3. #include <sys/types.h>  
  4. #include <sys/socket.h>  
  5. #include <sys/un.h>   
  6. #define UNIX_DOMAIN "/tmp/UNIX.domain"  
  7. int main(void)  
  8. {  
  9.     socklen_t clt_addr_len;  
  10.     int listen_fd;  
  11.     int com_fd;  
  12.     int ret;  
  13.     int i;  
  14.     static char recv_buf[1024];   
  15.     int len;  
  16.     struct sockaddr_un clt_addr;  
  17.     struct sockaddr_un srv_addr;  
  18.     listen_fd=socket(PF_UNIX,SOCK_STREAM,0);  
  19.     if(listen_fd<0)  
  20.     {  
  21.         perror("cannot create communication socket");  
  22.         return 1;  
  23.     }    
  24.       
  25.     //set server addr_param  
  26.     srv_addr.sun_family=AF_UNIX;  
  27.     strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);  
  28.     unlink(UNIX_DOMAIN);  
  29.     //bind sockfd & addr  
  30.     ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  31.     if(ret==-1)  
  32.     {  
  33.         perror("cannot bind server socket");  
  34.         close(listen_fd);  
  35.         unlink(UNIX_DOMAIN);  
  36.         return 1;  
  37.     }  
  38.     //listen sockfd   
  39.     ret=listen(listen_fd,1);  
  40.     if(ret==-1)  
  41.     {  
  42.         perror("cannot listen the client connect request");  
  43.         close(listen_fd);  
  44.         unlink(UNIX_DOMAIN);  
  45.         return 1;  
  46.     }  
  47.     //have connect request use accept  
  48.     len=sizeof(clt_addr);  
  49.     com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);  
  50.     if(com_fd<0)  
  51.     {  
  52.         perror("cannot accept client connect request");  
  53.         close(listen_fd);  
  54.         unlink(UNIX_DOMAIN);  
  55.         return 1;  
  56.     }  
  57.     //read and printf sent client info  
  58.     printf("/n=====info=====/n");  
  59.     for(i=0;i<4;i++)  
  60.     {  
  61.         memset(recv_buf,0,1024);  
  62.         int num=read(com_fd,recv_buf,sizeof(recv_buf));  
  63.         printf("Message from client (%d)) :%s/n",num,recv_buf);    
  64.     }  
  65.     close(com_fd);  
  66.     close(listen_fd);  
  67.     unlink(UNIX_DOMAIN);  
  68.     return 0;  
  69. }  

 

 

    使用套接字在UNIX域内实现进程间通信的客户端程序。相比服务端的程序,客户段较为简单。程序首先通过调用socket函数创建通信所需的套接字,然后,调用connect函数来连接服务器,在成功建立连接后,通过调用write函数向服务器发送指定的消息。

client代码:u_unix.c

 

[c-sharp]  view plain copy
  1. //c_unix.c  
  2. #include <stdio.h>  
  3. #include <sys/types.h>  
  4. #include <sys/socket.h>  
  5. #include <sys/un.h>  
  6. #define UNIX_DOMAIN "/tmp/UNIX.domain"  
  7. int main(void)  
  8. {  
  9.     int connect_fd;  
  10.     int ret;  
  11.     char snd_buf[1024];  
  12.     int i;  
  13.     static struct sockaddr_un srv_addr;  
  14. //creat unix socket  
  15.     connect_fd=socket(PF_UNIX,SOCK_STREAM,0);  
  16.     if(connect_fd<0)  
  17.     {  
  18.         perror("cannot create communication socket");  
  19.         return 1;  
  20.     }     
  21.     srv_addr.sun_family=AF_UNIX;  
  22.     strcpy(srv_addr.sun_path,UNIX_DOMAIN);  
  23. //connect server  
  24.     ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  25.     if(ret==-1)  
  26.     {  
  27.         perror("cannot connect to the server");  
  28.         close(connect_fd);  
  29.         return 1;  
  30.     }  
  31.     memset(snd_buf,0,1024);  
  32.     strcpy(snd_buf,"message from client");  
  33. //send info server  
  34.     for(i=0;i<4;i++)  
  35.         write(connect_fd,snd_buf,sizeof(snd_buf));  
  36.     close(connect_fd);  
  37.     return 0;  
  38. }  

 

 

编译运行:


[root@localhost liuxltest]# ./s_unix &
[1] 11664
[root@localhost liuxltest]# ./c_unix &

=====info=====
Message from client (1024)) :message from client
Message from client (1024)) :message from client
Message from client (1024)) :message from client
Message from client (1024)) :message from client
[2] 11665
[1]-  Done                    ./s_unix
[2]+  Done                    ./c_unix
[root@localhost liuxltest]#


当运行s_unix程序后,该程序将处于监听状态。这时,可以通过netstat命令查看程序运行情况,s_unix的套接字类型为流套接字,并处于监听状态。
[root@localhost liuxltest]# 
[root@localhost liuxltest]# ./s_unix &
[1] 12056
[root@localhost liuxltest]# netstat -an |grep /tmp
unix  2      [ ACC ]     STREAM     LISTENING     64014  /tmp/ssh-CekOJ11069/agent.11069
unix  2      [ ACC ]     STREAM     LISTENING     6216   /tmp/.font-unix/fs7100
unix  2      [ ACC ]     STREAM     LISTENING     62042  /tmp/ssh-XOCgkr9439/agent.9439
unix  2      [ ACC ]     STREAM     LISTENING     62316  /tmp/ssh-mojoaQ9648/agent.9648
unix  2      [ ACC ]     STREAM     LISTENING     65267  /tmp/UNIX.domain
unix  2      [ ACC ]     STREAM     LISTENING     65210  /tmp/ssh-NlKtA12012/agent.12012
[root@localhost liuxltest]# 

转载于:https://www.cnblogs.com/ylqmf/archive/2012/02/13/2349803.html

<think>我们正在讨论Windows下实现类似AF_UNIX进程间通信(IPC)。AF_UNIX在Linux中用于同一主机上的进程间通信,它通过文件系统路径名来标识通信端点。Windows没有原生的AF_UNIX支持,但我们可以使用其他机制来达到类似的效果。 根据引用[1],在Windows中,可以使用绑定到本地主机的AF_INET套接字进行进程间通信,并且Windows会优化这种通信(使用LRPC和内存映射文件实现零拷贝)。另外,引用[2]和[3]提到了其他IPC机制,如命名管道、共享内存等。 因此,在Windows下实现类似AF_UNIX的IPC,我们可以考虑以下几种方法: 1. **使用本地TCP/UDP套接字(AF_INET)**:创建套接字并绑定到127.0.0.1(本地回环地址)。这种方式在Windows中会被优化,性能较好。 2. **使用命名管道(NamedPipes)**:Windows提供了命名管道机制,支持可靠的双向通信。 3. **使用共享内存(Shared Memory)**:通过内存映射文件实现,适用于需要高效传输大量数据的场景。4.**使用Windows提供的其他IPC机制**:如邮件槽(Mailslots)、窗口消息(WindowMessages)等,但根据需求选择。下面我们将重点介绍前两种最常用的方法:本地套接字和命名管道。 ###方法1:使用本地TCP套接字(AF_INET)在Windows中,我们可以使用AF_INET套接字,绑定到127.0.0.1(本地回环地址),这样数据不会真正经过网络协议栈,而是由操作系统内部优化。引用[1]提到,Windows会检测到这种本地进程间的通信,并使用LRPC(轻量级远程过程调用)和内存映射文件来优化,实现零拷贝。####示例代码(C++) 以下是一个简单的TCP回显服务器和客户端的示例:**服务器端代码:**```c#include<winsock2.h> #include<iostream> #pragmacomment(lib,"ws2_32.lib") intmain(){WSADATA wsaData;WSAStartup(MAKEWORD(2,2), &wsaData);SOCKETserver =socket(AF_INET,SOCK_STREAM, IPPROTO_TCP);sockaddr_in addr;addr.sin_family= AF_INET;addr.sin_addr.s_addr =inet_addr("127.0.0.1");addr.sin_port= htons(12345);bind(server,(sockaddr*)&addr,sizeof(addr));listen(server,5);SOCKETclient =accept(server,NULL,NULL);charbuf[1024];intlen= recv(client, buf, sizeof(buf),0);send(client, buf, len,0);closesocket(client);closesocket(server);WSACleanup();return0;} ```**客户端代码:** ```c#include <winsock2.h>#include <iostream>#pragma comment(lib, "ws2_32.lib")int main() {WSADATAwsaData;WSAStartup(MAKEWORD(2,2),&wsaData);SOCKET client= socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);sockaddr_inaddr;addr.sin_family =AF_INET;addr.sin_addr.s_addr= inet_addr("127.0.0.1");addr.sin_port =htons(12345);connect(client,(sockaddr*)&addr,sizeof(addr));send(client,"Hello",5,0);char buf[1024];recv(client, buf, sizeof(buf),0);closesocket(client);WSACleanup();return0;} ```###方法2:使用命名管道(NamedPipes) 命名管道是Windows中一种强大的IPC机制,支持可靠的双向通信,并且可以跨不同进程(甚至不同机器,但这里我们只考虑本地)进行通信。####示例代码(C++)**服务器端(创建命名管道):** ```c#include <windows.h> #include<iostream> intmain(){HANDLEhPipe =CreateNamedPipe(L"\\\\.\\pipe\\MyPipe",//管道名称PIPE_ACCESS_DUPLEX,//双向访问PIPE_TYPE_BYTE| PIPE_WAIT,//字节模式、阻塞模式1,//最多1个实例1024,1024,//输入输出缓冲区大小0,//默认超时NULL//默认安全属性);if(hPipe ==INVALID_HANDLE_VALUE) {std::cerr <<"CreateNamedPipefailed:" <<GetLastError()<< std::endl;return1;}std::cout<< "Waiting forclient..."<< std::endl;if (ConnectNamedPipe(hPipe,NULL)){char buf[1024];DWORD read;ReadFile(hPipe,buf,sizeof(buf), &read,NULL);std::cout<< "Received:" <<buf <<std::endl;DWORD written;WriteFile(hPipe, buf, read, &written,NULL);}DisconnectNamedPipe(hPipe);CloseHandle(hPipe);return0; }``` **客户端(连接命名管道):** ```c#include <windows.h> #include<iostream> intmain(){HANDLEhPipe= CreateFile(L"\\\\.\\pipe\\MyPipe",//管道名称GENERIC_READ| GENERIC_WRITE,//读写权限0,//不共享NULL,//默认安全属性OPEN_EXISTING,//打开已存在的管道0,NULL);if (hPipe== INVALID_HANDLE_VALUE){std::cerr<< "CreateFilefailed:" <<GetLastError()<< std::endl;return1;}charmsg[] ="Hellofrom client";DWORDwritten;WriteFile(hPipe,msg,sizeof(msg), &written,NULL);charbuf[1024];DWORDread;ReadFile(hPipe,buf,sizeof(buf), &read,NULL);std::cout<< "Received:" <<buf <<std::endl;CloseHandle(hPipe);return0; }``` ###总结在Windows下实现类似AF_UNIX进程间通信,推荐使用:1. **本地TCP/UDP套接字**:适用于网络编程模型,代码可移植性好,且Windows内部有优化。2.**命名管道**:提供更高级的通信机制,支持可靠的双向通信,且具有较好的性能。 这两种方法都可以实现高效的本机进程间通信。具体选择取决于应用场景和开发者的熟悉程度。 §§相关问题§§1. Windows下本地TCP套接字和命名管道在性能上有何差异?2.除了套接字和命名管道,Windows还有哪些进程间通信机制?3.如何在Windows中实现类似Unix套接字(AF_UNIX)的权限控制(如文件系统权限)?</think>### Windows下实现类似AF_UNIX进程间通信方法 在Windows环境中实现类似Linux的AF_UNIX套接字功能,主要有以下三种高效方法: #### 1. **使用命名管道(Named Pipes)** [^2][^3] 命名管道是Windows原生支持的IPC机制,功能最接近AF_UNIX: ```c // 服务器端 HANDLE hPipe = CreateNamedPipe( L"\\\\.\\pipe\\MyPipe", // 管道名称 PIPE_ACCESS_DUPLEX, // 双向通信 PIPE_TYPE_BYTE | PIPE_WAIT, // 字节流模式 1, // 最大实例数 4096, 4096, // 输入/输出缓冲区 0, // 默认超时 NULL // 安全属性 ); // 客户端 HANDLE hPipe = CreateFile( L"\\\\.\\pipe\\MyPipe", // 管道名称 GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL ); ``` **特点**: - 支持双向字节流通信(类似SOCK_STREAM) - 通过命名路径标识(`\\.\pipe\名称`) - 支持身份验证和ACL权限控制 - 最大并发实例数可配置 #### 2. **优化本地TCP环回(AF_INET)** [^1] 通过绑定到`127.0.0.1`实现高效IPC,Windows内核会特殊优化: ```c // 服务器端 SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(12345); bind(sock, (sockaddr*)&addr, sizeof(addr)); // 客户端 connect(sock, (sockaddr*)&addr, sizeof(addr)); ``` **优化特性**: - Windows检测到本地通信时自动使用LRPC(轻量级RPC) - 通过内存映射文件实现零拷贝数据传输 - 通信栈跳过网络协议处理层 #### 3. **使用共享内存(Shared Memory)** 适用于高性能大数据传输: ```c // 创建共享内存 HANDLE hMap = CreateFileMapping( INVALID_HANDLE_VALUE, // 物理文件句柄 NULL, // 安全属性 PAGE_READWRITE, // 保护模式 0, // 高位大小 4096, // 低位大小 L"Local\\MySharedMemory" // 名称 ); // 映射视图 LPVOID pBuf = MapViewOfFile( hMap, // 句柄 FILE_MAP_ALL_ACCESS, 0, 0, 0 // 偏移量 ); ``` **适用场景**: - 需要超低延迟的进程间通信 - 传输大型数据(如图像、视频帧) - 配合信号量(Semaphore)实现同步 ### 方案对比 | 特性 | 命名管道 | 本地TCP | 共享内存 | |---------------------|------------------|------------------|---------------| | 通信模式 | 字节流/消息 | 字节流 | 内存块 | | 身份验证 | 支持 | 无 | 无 | | 零拷贝支持 | 部分 | 是[^1] | 是 | | 最大数据量 | 受缓冲区限制 | 受缓冲区限制 | 系统内存限制 | | 跨主机能力 | 否 | 是(需配置) | 否 | ### 开发建议 1. **优先选择命名管道** - 最接近AF_UNIX语义,自带连接管理 2. **需要网络兼容性时用本地TCP** - 便于未来扩展为网络通信 3. **超高性能场景用共享内存** - 但需自行实现同步机制 4. **.NET平台可用`NamedPipeServerStream`类** - 封装更友好 > 注意:Windows 10 Build 17063+ 开始原生支持[AF_UNIX套接字](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/),若目标系统版本>=1803可直接使用原生实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值