分类: LINUX
2016-03-01 14:24:10
使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。这里所指的使用套接字实现进程间通信,是由将通信域指定为PF_UNIX来实现的。该函数的形式如下:
int socket(int domain, int type, int protocol);
socket函数中的domain参数用于指定通信域,domain参数取PF_UNIX时,表示创建UNIX域的套接字。使用PF_UNIX域的套接字可以实现同一机器上的不同进程间的通信。
调用bind函数实现了套接字与地址(这里是文件名)的绑定。bind函数的具体信息如下:
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
其参数my_addr为指向结构体sockaddr_un的指针,该结构体的定义如下:
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; /*PF_UNIX或AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 路径名 */
};
在该结构体中,sun_family为AF_UNIX。sun_path是套接字在文件系统中的路径名。
Unix域套接字是通过套接字API实现的简单的协议族。实际上它并不代表一个网络协议;它只能连接到同一台机器上的套接字。它提供了灵活的IPC机制。它的地址是它所在的文件系统的路径名,创建之后套接字就和路径名绑定在一起。用来表示Unix域地址的套接字文件能够使用stat()但是不能通过open()打开,而且应该使用套接字API对它进行操作。
Unix域套接字是面向连接的,每个套接字的连接都建立了一个新的通讯信道。服务器可能同时处理许多连接,但对于每个连接都有不同的文件描述符。这个属性使Unix域套接字能够比命名管道更好的适应IPC任务。
.
在一个终端运行服务器,然后在另一个终端(在相同目录下)运行客户端。当从客户端输入一行时,数据将通过套接字送到服务器。当退出客户端,服务器将等待另外一个连接。还可以通过客户端程序的重定向输入来传送文件,cat uclient.c | ./uclient 或 ./uclient < uclient.c。
服务器程序userver.c
/* userver.c - Simple Unix Domain Socket server */
/* Waits for a connection on the ./sample-socket Unix domain
socket. Once a connection has been established, copy data
from the socket to stdout until the other end closes the
connection, and then wait for another connection to the
socket. */
#include
#include
#include
#include
#include "sockutil.h" /* some utility functions */
int main(void) {
struct sockaddr_un address;
int sock, conn;
size_t addrLength;
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
die("socket");
/* Remove any preexisting socket (or other file) */
unlink("./sample-socket");
address.sun_family = AF_UNIX; /* Unix domain socket */
strcpy(address.sun_path, "./sample-socket");
/* The total length of the address includes the sun_family
element */
addrLength = sizeof(address.sun_family) +
strlen(address.sun_path);
if (bind(sock, (struct sockaddr *) &address, addrLength))
die("bind");
if (listen(sock, 5))
die("listen");
while ((conn = accept(sock, (struct sockaddr *) &address,
&addrLength)) >= 0) {
printf("---- getting data\n");
copyData(conn, 1);
printf("---- done\n");
close(conn);
}
if (conn < 0)
die("accept");
close(sock);
return 0;
}
客户端程序uclient.c
/* uclient.c - Simple Unix Domain Socket client */
/* Connect to the ./sample-socket Unix domain socket, copy stdin
into the socket, and then exit. */
#include
#include
#include
#include "sockutil.h" /* some utility functions */
int main(void) {
struct sockaddr_un address;
int sock;
size_t addrLength;
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
die("socket");
address.sun_family = AF_UNIX; /* Unix domain socket */
strcpy(address.sun_path, "./sample-socket");
/* The total length of the address includes the sun_family
element */
addrLength = sizeof(address.sun_family) +
strlen(address.sun_path);
if (connect(sock, (struct sockaddr *) &address, addrLength))
die("connect");
copyData(0, sock);
close(sock);
return 0;
}
注:如果客户端在服务器bind之前进行connect操作,connect会因为缺少套接字文件而失败。
accept和connect是两个阻塞调用。
/* sockutil.c - Utility functions used in socket example programs */
#include
#include
#include
#include "sockutil.h"
/* issue an error message via perror() and terminate the program */
void die(char * message) {
perror(message);
exit(1);
}
/* Copies data from file descriptor 'from' to file descriptor
'to' until nothing is left to be copied. Exits if an error
occurs. This assumes both from and to are set for blocking
reads and writes. */
void copyData(int from, int to) {
char buf[1024];
int amount;
while ((amount = read(from, buf, sizeof(buf))) > 0) {
if (write(to, buf, amount) != amount) {
die("write");
return;
}
}
if (amount < 0)
die("read");
}