UNIX域套接字轮询XSI消息队列

来源:互联网 发布:淘宝怎么拒收退款流程 编辑:程序博客网 时间:2024/05/22 02:06

UNIX域套接字用于在同一台计算机上运行的进程之间的通信。这点比用因特网域套接字效率更高。因为UNIX域套接字仅仅复制数据,并不执行协议处理,不要添加或删除网络报头,无需计算校验和, 不要产生顺序号,无需发送确认报文。 UNIX域套接字提供流和数据包两种接口。UNIX域数据报服务是可靠的,既不会丢失报文,也不会传递出错。UNIX域套接字就像是套接字和管道的混合。可以使用它们面向网络的域套接字接口或使用socketpair函数创建一对无命名的、相互连接的UNIX域套接字。

#include <sys/socket.h>int socketpair(int domain, int type, int protocol, int sockfd[2]);                            返回值: 若成功,返回0; 若出错,返回-1

一对相互连接的UNIX域套接字可以起到全双工通道的作用:两端对读写都开放。
这里写图片描述

消息队列是XSI IPC三种方式之一(其他两个:信号量、共享存储)。每个内核中的IPC结构都用一个非负整数的标识符加以引用。例如, 要向一个消息队列发送消息或者从一个消息队列取消息,只需要知道其队列标识符。与文件描述符不同,IPC标识符不是小的整数。 当一个IPC结构被创建时,与这种结构相关的标识符连续加1,直至达到一个整型数的最大正值,然后又回转到0。

标识符是IPC对象的内部名。为使多个合作进程能够在同一IPC对象上汇聚,提供了一个外部命名方案。 每个IPC对象都与一个键相关联,将这个键作为该对象的外部名。当创建一个新IPC结构时,都应该指定一个键,这个键的数据类型是基本系统数据类型key_t.

但是,消息队列不能关联到文件描述符,这就限制了它不能和poll或select函数一起使用。然而,套接字是和文件描述符相关联的,消息到达时,可以用套接字来通知。对每一个消息队列使用一个线程。每个线程都会在msgrcv调用中阻塞。当消息到达时,线程会把它写入一个UNIX域套接字的一端。当poll指示套接字可以读数据时,应用程序会使用这个套接字的另一端接收消息。

// 17-3.c//使用UNIX域套接字轮询 XSI消息队列#include "apue.h"#include "myerror.h"#include <pthread.h>#include <sys/msg.h>#include <sys/socket.h>#include <sys/poll.h>#define NQ      3       /* number of queues */#define MAXMSZ  512     /* maximum message size */#define KEY     0x123   /* key for first message queue */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");        printf("\n");        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);}

在main函数中创建了一些消息队列和UNIX域套接字,并为每个消息队列开启了一个线程。然后它在一个无线循环中用poll来论询选择一个套接字端点。当某个套接字可读时,程序从套接字中读取数据并把消息打印到标准输出上。

//17-4.c//给XSI消息队列发送消息//程序需要两个参数: 消息队列关联的键值以及一个包含消息主体的字符串。#include "apue.h"#include "myerror.h"#include <sys/msg.h>#define MAXMSZ 512struct 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);}

测试结果:
这里写图片描述

0 0
原创粉丝点击