作者在 2011-05-18 11:43:59 发布以下内容
使用socket实现进程间通信:(UNIX domain中面向连接通信)
使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。
man unix内容如下:
NAME( 名称)
unix, PF_UNIX, AF_UNIX, PF_LOCAL, AF_LOCAL ? 用于本地内部进程通讯的套接 字。
SYNOPSIS( 总览 )
#include <sys/socket.h>
#include <sys/un.h>
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
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 中传递无效的信用证明.
当生成一个文件系统套接字对象时, 可能会由通用套接层或者文件系统产生其它错误. 要获得更多信息,可参见合适的手册页.
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
//s_unix.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
socklen_t clt_addr_len;
int listen_fd;
int com_fd;
int ret;
int i;
static char recv_buf[1024];
int len;
struct sockaddr_un clt_addr;
struct sockaddr_un srv_addr;
listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
if(listen_fd<0)
{
perror("cannot create communication socket");
return 1;
}
//set server addr_param
srv_addr.sun_family=AF_UNIX;
strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);
unlink(UNIX_DOMAIN);
//bind sockfd & addr
ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-1)
{
perror("cannot bind server socket");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//listen sockfd
ret=listen(listen_fd,1);
if(ret==-1)
{
perror("cannot listen the client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//have connect request use accept
len=sizeof(clt_addr);
com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
if(com_fd<0)
{
perror("cannot accept client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//read and printf sent client info
printf("\n=====info=====\n");
for(i=0;i<4;i++)
{
memset(recv_buf,0,1024);
int num=read(com_fd,recv_buf,sizeof(recv_buf));
printf("Message from client (%d)) :%s\n",num,recv_buf);
}
close(com_fd);
close(listen_fd);
unlink(UNIX_DOMAIN);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
socklen_t clt_addr_len;
int listen_fd;
int com_fd;
int ret;
int i;
static char recv_buf[1024];
int len;
struct sockaddr_un clt_addr;
struct sockaddr_un srv_addr;
listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
if(listen_fd<0)
{
perror("cannot create communication socket");
return 1;
}
//set server addr_param
srv_addr.sun_family=AF_UNIX;
strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);
unlink(UNIX_DOMAIN);
//bind sockfd & addr
ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-1)
{
perror("cannot bind server socket");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//listen sockfd
ret=listen(listen_fd,1);
if(ret==-1)
{
perror("cannot listen the client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//have connect request use accept
len=sizeof(clt_addr);
com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
if(com_fd<0)
{
perror("cannot accept client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//read and printf sent client info
printf("\n=====info=====\n");
for(i=0;i<4;i++)
{
memset(recv_buf,0,1024);
int num=read(com_fd,recv_buf,sizeof(recv_buf));
printf("Message from client (%d)) :%s\n",num,recv_buf);
}
close(com_fd);
close(listen_fd);
unlink(UNIX_DOMAIN);
return 0;
}
使用套接字在UNIX域内实现进程间通信的客户端程序。相比服务端的程序,客户段较为简单。程序首先通过调用socket函数创建通信所需的套接字,然后,调用connect函数来连接服务器,在成功建立连接后,通过调用write函数向服务器发送指定的消息。
client代码:c_unix.c
//c_unix.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
int connect_fd;
int ret;
char snd_buf[1024];
int i;
static struct sockaddr_un srv_addr;
//creat unix socket
connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
if(connect_fd<0)
{
perror("cannot create communication socket");
return 1;
}
srv_addr.sun_family=AF_UNIX;
strcpy(srv_addr.sun_path,UNIX_DOMAIN);
//connect server
ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-1)
{
perror("cannot connect to the server");
close(connect_fd);
return 1;
}
memset(snd_buf,0,1024);
strcpy(snd_buf,"message from client");
//send info server
for(i=0;i<4;i++)
write(connect_fd,snd_buf,sizeof(snd_buf));
close(connect_fd);
return 0;
}
编译运行:
[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]#
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]#