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

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 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);

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值