IPC之消息队列·即时通讯小程序(一)

发布时间:2020-08-06 05:52:22 作者:SherryX
来源:网络 阅读:14199

消息队列

上次说到了进程间通信的管道,不过匿名管道有个缺点就是,只能做到有亲缘关系的进程间通信,所以今天学习一个新的进程间通信方式——消息队列。

msgrcv

作用:从一个消息队列里检索消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指向准备接收的消息
msgsz:msgp指向的消息长度,这个长度不能保存消息类型里的“long int”类型(下面会说)
msgflg:控制着队列中没有相应类型的消息可供接收的时候将要发生的事
msgtyp:可以实现接收优先级的简单形式
返回值:成功-返回实际放到接收缓冲区里的字符个数,失败- “-1”
msgflg有以下几个值:
IPC之消息队列·即时通讯小程序(一)

IPC之消息队列·即时通讯小程序(一)
IPC之消息队列·即时通讯小程序(一)
msgid_ds数据结构定义如下:

 struct msqid_ds {
               struct ipc_perm msg_perm;    /* Ownership and permissions */
               time_t         msg_stime;    /* Time of last msgsnd(2) */
               time_t         msg_rtime;    /* Time of last msgrcv(2) */
               time_t         msg_ctime;    /* Time of last change */
               unsigned long  __msg_cbytes; /* Current number of bytes in
                                               queue (non-standard) */
               msgqnum_t      msg_qnum;     /* Current number of messages
                                               in queue */
               msglen_t       msg_qbytes;   /* Maximum number of bytes
                                               allowed in queue */
               pid_t          msg_lspid;    /* PID of last msgsnd(2) */
               pid_t          msg_lrpid;    /* PID of last msgrcv(2) */
           };

练习

试着写一段吧~
发送端:msgsnd.c

struct msg_t
{
    long mtype; //第一个必须是long,>=1
    char acMsg[20];
};

//用ipcs 
int main()
{
    int msgid;
    struct msg_t msg = {0};

    //消息队列的创建、打开、删除
    msgid = msgget(1000,IPC_CREAT);
    if(msgid == -1)
    {
        perror("create msg");
    }
    printf("msgid = %d\n",msgid);

    msg.mtype = 1;
    strcpy(msg.acMsg,"hello");
    msgsnd(msgid,&msg,sizeof(struct msg_t)-sizeof(long),0);

    //msgctl(msgid,IPC_RMID,NULL);//也可以用命令ipcrm

    return 0;
}

接收端:msgrcv.c

struct msg_t
{
    long mtype; //第一个必须是long,>=1
    char acMsg[20];
};

int main()
{
    int msgid;
    struct msg_t msg = {0};

    //消息队列的创建、打开、删除
    msgid = msgget(1000,0);
    if(msgid == -1)
    {
        perror("open msg!\n");
    }
    printf("msgid = %d\n",msgid);

    msgrcv(msgid,&msg,sizeof(struct msg_t)-sizeof(long),1,0);
    printf("recv msg: %s.\n",msg.acMsg);

    return 0;
}

运行:
开三个终端,一个运行msgsnd.c,一个运行msgrcv.c,一个用来查看消息队列状态:
1、先发送:
IPC之消息队列·即时通讯小程序(一)
2、查看:ipcs
IPC之消息队列·即时通讯小程序(一)
3、读取:
IPC之消息队列·即时通讯小程序(一)
4、再查看(被取走了):
IPC之消息队列·即时通讯小程序(一)
<br>
但是,也注意到了使用消息队列,消息一旦被读走,就没了。

即时通讯小程序

使用消息队列与共享内存(后面会复习)完成一个简单的终端聊天程序,要求如下。
1.程序有一个server服务器,服务器有一个在线列表,当终端登入时,将终端的进程ID作为用户名称添加到在线列表。(消息队列和共享内存)
2.终端登入时,获取用户的在线列表。(消息队列、信号、和共享内存)
3.终端登入后,进入聊天状态。:(信号、共享内存)
输入#chat [pid],进入私聊模式
例如:#chat 1234,与终端1234进入私聊。只有终端1234才能接收消息。
输入#chat 0,进入群聊模式。全部终端可以接收消息。
说明:1,2,3为基本功能,要求实现。4,5,6为附加功能,有能力同学可以尝试实现。
4.终端登入时,服务器发送消息,通知其他在线终端,更新在线列表。(信号)
5.终端在聊天状态,输入#user,列出在线用户列表。
6.终端在聊天状态,输入#exit,终端退出,服务器将终端的进程ID移出在线列表,并通知在线终端,更新在线列表。(消息队列和共享内存)

学了消息队列,至少可以把用户的登录、退出完成。
代码如下:
public.h

#ifndef _PUBLIC_H_
#define _PUBLIC_H_

#include < stdio.h>
#include < string.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/msg.h>

typedef struct login_t
{
    long type;
    pid_t pid;
}LOGIN_T;

#define MSG_KEY 1
#define MSG_SIZE sizeof(LOGIN_T)-sizeof(long)

#endif

server.c

#include " public.h"

int main()
{
    int msg_id;
    LOGIN_T login = {0};

    //创建用户的消息队列
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        msg_id = msgget(MSG_KEY,IPC_CREAT);
        if (msg_id == -1)
        {
            perror("server msgget");
            return -1;
        }
    }
    //一直监听,是否有用户上线
    while (1)
    {
        memset(&login,0,sizeof(LOGIN_T));
        msgrcv(msg_id,&login,MSG_SIZE,0,0); //任何消息都接收
        switch(login.type)
        {
        case 1:
            printf("client %d is logining...\n",login.pid);
            break;
        case 2:
            printf("client %d is exiting...\n",login.pid);
            break;

        }

    }

    return 0;
}

client.c

#include "public.h"

int main()
{
    char acBuf[20] = "";
    int msg_id;
    LOGIN_T login = {0};

    //打开消息队列
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        perror("client msgget");
        return -1;
    }
    //登录,写消息队列
    login.type = 1;     //设置登录的消息类型为1
    login.pid = getpid();
    printf("%d is logining...\n",login.pid);
    msgsnd(msg_id,&login,MSG_SIZE,0);

    //等待写
    while(1)
    {
        putchar('#');
        fflush(stdout);
        scanf("%s",acBuf);
        if (strcmp(acBuf,"quit") == 0)
        {
            login.type = 2;     //设置退出的消息类型为2
            msgsnd(msg_id,&login,MSG_SIZE,0);
            break;
        }
    }
    return 0;
}

运行:先运行服务器端,再运行多个客户端
客户端:
IPC之消息队列·即时通讯小程序(一)
服务器:
IPC之消息队列·即时通讯小程序(一)

推荐阅读:
  1. IPC之信号量·即时通讯小程序(三)
  2. IPC之共享内存·即时通讯小程序(二)

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

进程间 通信 消息队列

上一篇:传统STP、快速STP、MSTP的总结和比较

下一篇:sige压力测试和hping3发包工具(初稿)

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》