1. UNIX域套接字
用于同一台计算机上运行的进程之间的通信。提供流和数据报两种接口,UNIX域数据报服务是可靠的。
int socketpair(int domain, int type, int protocol, int sockfd[2]); //创建一对无命名的,相互连接的UNIX域套接字
//借助UNIX域套接字轮询XSI消息队列
<span style="font-size:18px;">#include "apue.h"
#include <poll.h>
#include <pthread.h>
#include <sys/msg.h>
#include <sys/socket.h>
#define NQ 3
#define MAXMSZ 512
#define KEY 0x123
struct threadinfo{
int qid;
int fd;
};
struct mymesg{
long mtype;
char mtext[MAXMSZ];
};
void *helper(void *arg)
{
int n;
struct mymesg m;
struct threadinfo *tip = arg;
for(;;) {
memset(&m, 0, sizeof(m));
if((n = msgrcv(tip->qid, &m, MAXMSZ, 0, MSG_NOERROR)) < 0)
err_sys("msgrcv error");
if(write(tip->fd, m.mtext, n) < 0)
err_sys("write error");
}
}
int main()
{
int i, n, err;
int fd[2];
int qid[NQ];
struct pollfd pfd[NQ];
struct threadinfo ti[NQ];
pthread_t tid[NQ];
char buf[MAXMSZ];
for(i = 0; i < NQ; i++) {
if((qid[i] = msgget((KEY+i), IPC_CREAT|0666)) < 0)
err_sys("msgget error");
printf("queue ID %d is %d\n", i, qid[i]);
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, fd) < 0)
err_sys("socketpair error");
pfd[i].fd = fd[0];
pfd[i].events = POLLIN;
ti[i].qid = qid[i];
ti[i].fd = fd[1];
if((err = pthread_create(&tid[i], NULL, helper, &ti[i])) != 0)
err_exit(err, "pthread_create error");
}
for(;;) {
if(poll(pfd, NQ, -1) < 0)
err_sys("poll error");
for(i = 0; i < NQ; i ++) {
if(pfd[i].revents & POLLIN) {
if((n = read(pfd[i].fd, buf, sizeof(buf))) < 0)
err_sys("read error");
buf[n] = 0;
printf("queue id %d, message %s\n", qid[i], buf);
}
}
}
exit(0);
}
</span>
<span style="font-size:18px;">#include "apue.h"
#include <sys/msg.h>
#define MAXMSZ 512
struct mymesg{
long mtype;
char mtext[MAXMSZ];
};
int main(int argc, char *argv[])
{
key_t key;
long qid;
size_t nbytes;
struct mymesg m;
if(argc != 3) {
fprintf(stderr, "usage: sendmsg KEY message\n");
exit(1);
}
key = strtol(argv[1], NULL, 0);
if((qid = msgget(key, 0)) < 0)
err_sys("can't open queue key %s", argv[1]);
memset(&m, 0, sizeof(m));
strncpy(m.mtext, argv[2], MAXMSZ - 1);
nbytes = strlen(m.mtext);
m.mtype = 1;
if(msgsnd(qid, &m, nbytes, 0) < 0)
err_sys("can't send message");
exit(0);
}
</span>
命名UNIX域套接字
//将地址绑定到UNIX域套接字,如果我们试图绑定一个已经存在的文件,那么bing请求会失败。
<span style="font-size:18px;">#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
int main(void)
{
int fd, size;
struct sockaddr_un un;
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "foo.socket");
if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
err_sys("socket failed");
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
if(bind(fd, (struct sockaddr *)&un, size) < 0)
err_sys("bind error");
printf("UNIX domain socket bound\n");
exit(0);
}
</span>
//使用以下三个函数,在同一台计算机上的两个无关进程间创建唯一连接。
int serv_listen(const char *name); //若成功,返回要监听的文件描述符;若出错,返回负值。
int serv_accept(int listenfd, uid_t *uidptr); //若成功,返回新文件描述符;若出错,返回负值。
int cli_conn(const char *name); //若成功,返回文件描述符;若出错,返回负值。
//serv_listen声明它要在一个众所周知的名字上监听客户进程的连接请求
<span style="font-size:18px;">#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define QLEN 10
int serv_listen(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un;
if(strlen(name) >= sizeof(un.sun_path)) {
errno = ENAMETOOLONG;
return -1;
}
//create a UNIX domain stream socket
if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -2;
//ensure it's not already exists
unlink(name);
//fill in socket address structure
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
//bind the name to the descriptor
if(bind(fd, (struct sockaddr *)&un, len) < 0) {
rval = -3;
goto errout;
}
//tell kernel we're a server
if(listen(fd, QLEN) < 0) {
rval = -4;
goto errout;
}
return (fd);
errout:
err = errno;
close(fd);
errno = err;
return rval;
} </span>
<span style="font-size:18px;">#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <errno.h>
#define STALE 30 //client's name can't be older than this
/******************************************************
Wait for a client connnection to arrive, and accept it.
We also obtain the client's user ID from the pathname
that it must bind before calling us.
Returns new fd if all is OK, < 0 on error
******************************************************/
int serv_accept(int listenfd, uid_t *uidptr)
{
int clifd, err, rval;
socklen_t len;
time_t staletime;
struct sockaddr_un un;
struct stat statbuf;
char *name;
// allocate enough space for longest name plus terminating null
if((name = malloc(sizeof(un.sun_path) + 1)) == NULL)
return -1;
len = sizeof(un);
if((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
free(name);
return -2;
}
// obtain the client's uid from its calling address
len -= offsetof(struct sockaddr_un, sun_path);
memcpy(name, un.sun_path, len);
name[len] = 0;
if(stat(name, &statbuf) < 0) {
rval = -3;
goto errout;
}
#ifdef S_ISSOCK
if(S_ISSOCK(statbuf.st_mode) == 0) {
rval = -4;
goto errout;
}
#endif
if((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || (statbuf.st_mode & S_IRWXU) != S_IRWXU) {
rval = -5;
goto errout;
}
staletime = time(NULL) - STALE;
if(statbuf.st_atime < staletime || statbuf.st_ctime < staletime || statbuf.st_mtime < staletime) {
rval = -6;
goto errout;
}
if(uidptr != NULL)
*uidptr = statbuf.st_uid;
unlink(name);
free(name);
return clifd;
errout:
err = errno;
close(clifd);
free(name);
errno = err;
return rval;
}
</span>
//cli_conn函数连接至服务器进程
#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define CLI_PATH "/var/tmp"
#define CLI_PERM S_IRWXU
/*********************************************
create a client endpoint and connect to server
return fd if all OK, < 0 on error.
*********************************************/
int cli_conn(const char *name)
{
int fd, len, err, rval;
struct sockaddr_un un, sun;
int do_unlink = 0;
if(strlen(name) >= sizeof(un.sun_path)) {
errno = ENAMETOOLONG;
return -1;
}
//create a UNIX domain stream socket
if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return -1;
//fill socket address structure with our address
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "%s%05ld", CLI_PATH, (long)getpid());
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
unlink(un.sun_path);
if(bind(fd, (struct sockaddr *)&un, len) < 0) {
rval = -2;
goto errout;
}
if(chmod(un.sun_path, CLI_PERM) < 0) {
rval = -3;
do_unlink = 1;
goto errout;
}
//fill socket address structure with server's address
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
if(connect(fd, (struct sockaddr *)&sun, len) < 0) {
rval = -4;
do_unlink = 1;
goto errout;
}
return fd;
errout:
err = errno;
close(fd);
if(do_unlink)
unlink(un.sun_path);
errno = err;
return rval;
}
3. 传送文件描述符
int send_fd(int fd, int fd_to_send); //使用fd代表的UNIX域套接字发送描述符fd_to_send
int send_err(int fd, int status, const char *errmsg); //使用fd发送msgerr以及后随的status字节
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t)); //客户进程用此来接受描述符
#include "apue.h"
/********************************************************
Used when we had planned to send an fd using send_fd(),
but encountered an error instead. We send the error back
using the send_fd()/recv_fd() protocol.
********************************************************/
int send_err(int fd, int errcode, const char *msg)
{
int n;
if((n = strlen(msg)) > 0)
if(writen(fd, msg, n) != n)
return -1;
if(errcode >= 0)
errcode = -1;
if(send_fd(fd, errcode) < 0)
return -1;
return 0;
}
send_fd(P649), recv_fd(P651)。