【通信篇】【qnx通信机制】

本文详细介绍了QNX操作系统中基于通道的同步消息传递机制,包括name_attach、MsgReceive、MsgReply、name_open和MsgSend等关键方法的使用,以及客户端和服务端的状态迁移过程。
摘要由CSDN通过智能技术生成

系列文章

可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm=1001.2014.3001.5501


目录

系列文章

1.简介

1.1 交互图

1.2 状态迁移图

1.2.1 客户端迁移图

 1.2.2 服务端迁移图

2.方法介绍

2.1 name_attach

 2.2 name_detach

 2.3 MsgReceive

 2.4 MsgReply

 2.5 name_open

 2.6 MsgSend

3.如何使用?

3.1 服务端

3.2 客户端



1.简介

QNX是一个微内核的操作系统,操作系统中,因为每一个进程都拥有自己的独立的进程虚拟地址空间,造成了进程独立性。但进程间总会需要进行交互,因此进程间的通信是难免的。常见的进程间通信有:socket、信号、信号量、andorid特有的binder通信,共享内存等。

本文主要介绍qnx操作系统的同步消息传递机制。

通信步骤和线程状态如下:

1.因为进程间消息的传递通过通道(channel)连接,所以首先服务端需要通过name_attach创建channel,然后调用MsgReceive接收消息。此时客户端没有发送消息,则服务端阻塞,服务端阻塞状态是RECEIVE blocked。

2.客户端调用name_open打开并连接服务端。

3.客户端调用MsgSend发送消息到服务端,若此时服务端没有调用MsgReceive(),则客户端状态是SEND blocked,若此时服务端调用了MsgReceive(),但是服务端还没调用MsgReply()/MsgError(),则客户端处于REPLY blocked。

4.服务端接收到客户端发送的消息后处理消息,此时服务端状态为ready状态。并调用MsgReply回复client。

1.1 交互图

1.2 状态迁移图

1.2.1 客户端迁移图

 send blocked:当客户端调用Msgsend函数后,服务端并没有调用MsgReceive的状态。

reply blocked:当客户端调用Msgsend函数后,服务端也调用了MsgReceive,但服务端还未调用

MsgReply/MsgError的状态。

ready:当客户端调用Msgsend函数后,服务端也调用了MsgReceive和MsgReply/MsgError的状态。


 1.2.2 服务端迁移图

 receive blocked:服务端调用MsgRecevie,但是客户端没调用Msgsend的状态。

ready:服务端调用MsgRecevie,然后客户端调用Msgsend的状态。

2.方法介绍

2.1 name_attach

//在路径名空间中注册一个名称并创建一个通道
name_attach_t * name_attach( dispatch_t * dpp,
                             const char * path,
                             unsigned flags );
//dpp可以是null,或者是成功调用dispatch_create()或dispatch_ccreate_channel()返回的调度句柄。
//path是要在/dev/name/[local|global]/下注册的路径。
//flags 这是控制函数行为的标志位。如果是NAME_FLAG_ATTACH_GLOBAL,代表创建全局的附加名称。默认附在本地
//返回值:返回一个指向name_attach_t结构的指针,结构如下:
//如果失败,则返回为空


typedef struct _name_attach {
dispatch_t* dpp; //创建此连接时使用的调度句柄。
int         chid; //直接用于MsgReceive()的通道ID。
int         mntid;
int         zero[2];
} name_attach_t;

 2.2 name_detach

//函数的作用是从名称空间中删除名称,并破坏name_attach()创建的通道。
//如果在标志中设置NAME_FLAG_DETACH_SAVEPP,则NAME_attach_t结构中包含的调度指针不会被破坏;
//由您通过调用dispatchdestroy()来销毁它。默认情况是销毁调度指针。

int name_detach( name_attach_t * attach, 
                 unsigned flags );
//attach:成功调用name_attach()返回的指向name_attach_t结构的指针。
//flag:NAME_FLAG_DETACH_SAVEPP--不要破坏调度句柄。

 2.3 MsgReceive

//接收来自其他进程的消息,这些消息通常通过MsgSend函数发送,当调用MsgReceive时,它会阻塞调用线程,直到有消息可用或者发生错误为止。
int MsgReceive( int chid,
                void * msg,
                size_t bytes,
                struct _msg_info * info );
//chid是已经建立链接的频道id
//msg指向要发送的消息的地址
//bytes,发送消息的大小
//info,可以为Null,或者指向_msg_info结构的指针,函数可以在该结构中存储有关消息的附加信息。
//返回值:
//-1,代表通信发生错误
//0代表是脉冲消息
//大于0,代表收到了客户端发送的消息

 2.4 MsgReply

//当一个进程接收到另一个进程发送的消息后,它可以使用MsgReply函数来发送一个回复消息。
int MsgReply(int rcvid, int status, const void *reply, size_t rsize);
参数说明:

//rcvid:是接收消息的ID,通常这个ID是在调用MsgReceive时获得的。
//status:是一个整数,表示回复消息的状态码。通常,它用于指示消息处理的结果或状态。
//reply:是一个指向回复消息内容的指针。如果不需要发送任何数据,这个指针可以设置为NULL。
//rsize:指定了回复消息的大小,即reply指针指向的数据的大小。
//返回值:
//0代表发送成功。
//-1代表发送失败。

 2.5 name_open

int name_open( const char * name, 
               int flags );
//name :连接服务端的频道名称,在服务端name_attach时会指定。
//flag:一些策略,如安全连接等。。
//返回值:
//-1,代表发生错误
//大于0,代表正确连接上服务端,返回的是通道连接id

 2.6 MsgSend

int MsgSend(int coid, const void *smsg, int sbytes, void *rmsg, int rbytes);
//参数说明:

//coid:接收消息的通道的ID。
//smsg:指向要发送的消息的指针。
//sbytes:要发送的消息的字节数。
//rmsg:指向用于接收来自接收方的回复消息的缓冲区的指针(如果不需要回复,则此参数可以为NULL)。
//rbytes:接收缓冲区的大小(如果不需要回复,则此参数可以为0)。

//返回值:

//如果成功,MsgSend函数返回接收到的回复消息的字节数(如果提供了回复缓冲区的话)。
//如果失败,返回-1,并设置全局变量errno以指示错误原因。

3.如何使用?

3.1 服务端

步骤:

第一步:服务端通过name_attach,建立一个频道

第二步:自定义结构体,此结构体需和客户端定义一样,其中包含通信的信息。

第三步:通过MsgReceive,用自定义的结构体接受客户端消息。

第四步:解析消息,进行相应处理。

typedef struct
{
    struct _pulse hdr;//表示脉搏心跳
    uint32_t internalcmd;
    uint32_t data1size;
    uint8_t data1[MAX_DATAID_SIZE];
    uint32_t data2size;
    uint8_t data2[MAX_PAYLOAD_SIZE];
} stIpcMessage;//此结构体类型是自定义的,需要和客户端定义的结构体类型相同

void loop()
{
    //第一步创建name_attach_t,建立一个频道
    name_attach_t *nameattach; 
    if ((nameattach = name_attach(NULL, "testservice", 0)) == NULL)
    //name_attach()就是用来建立一个频道,
并为频道注册一个名字testservice
    //name_attach():是在服务器端使用,在名称空间中定义一个name(客户端中对应open这个name),
    //同时创建了一个channel.        
     {
            return;
     }
     
     int receiveid;
     stIpcMessage ipcmsg;//第二步:自定义结构体,此结构体需和客户端定义一样,其中包含通信的信息。
     //第三步:死循环不断的接受客户端的消息,用ipcmsg接收消息
     while (true)
    {
         receiveid = MsgReceive(attach->chid, &ipcmsg, sizeof(ipcmsg), NULL);
         //使用MsgReceive函数接收客户端的信息。attach->chid是channelid
         
         if (receiveid == -1)//代表MsgReceive通信错误,退出循环
         {
            break;
         }
         if (receiveid == 0)//0表示收到是脉搏信息,>0表示收到message
         { 
            switch (ipcmsg.hdr.code)
            {
                case _PULSE_CODE_DISCONNECT://客户端断开了所有连接(对于我们名称中的每个name_open()调用name_close())或终止
                    ConnectDetach(ipcmsg.hdr.scoid);//终止和客户端的连接
                    break;
                case _PULSE_CODE_UNBLOCK://REPLY BLOCK中的xx线程希望脱离阻塞状态”。
                //然后,服务器会根据自身的逻辑和规则来判断如何处理这个请求         
                    break;
                default:
                
                    break;
            }
                continue;
          }
          
          if (ipcmsg.hdr.type == _IO_CONNECT)
          //当客户端用name_open连接上服务端的时候,会发送一个connect的消息
          {
                MsgReply(receiveid, EOK, NULL, 0);
                continue;
          }

 
        //收到的一些错误的message
        if (ipcmsg.hdr.type > _IO_BASE && ipcmsg.hdr.type <= _IO_MAX)
        {
            MsgError(receiveid, ENOSYS);
            continue;
        }

        switch (ipcmsg.internalcmd)//判断msg的字段
        {
            case REQ_A://是A类型请求,则
            {
                //第四步:处理消息
                std::string data1((char*)&ipcmsg.data1[0], msg.data1size);/
                MsgReply(rcvid, EOK, NULL, 0);//向客户端回复收到消息
            }
            break;
        }
}



int main()
{
    loop();
}

3.2 客户端

步骤:

第一步:通过name_open连接服务端,传入的参数正是服务端注册的频道的名称。

第二步:封装自定义的结构体,此结构体和服务端一样,通过MsgSend发送数据。

第三步:发送完后,可以关闭频道。

typedef struct
{
    struct _pulse hdr;//服务端会根据值判断传递的消息是否出错。
    uint32_t internalcmd;//服务端根据此字段判断是什么类型
    uint32_t data1size;
    uint8_t data1[MAX_DATAID_SIZE];
    uint32_t data2size;
    uint8_t data2[MAX_PAYLOAD_SIZE];
} IpcMessage;//此结构体类型是自定义的,需要和客户端定义的结构体类型相同

int ChannelId;

//第一步连接服务端
void connectservice()
{
    ChannelId=name_open("testservice", 0));//此处需要对应服务端的名字
}

//第二步:封装结构体,发送数据
void sendmsg()
{
    IpcMessage sendmsg;
    data1 = "data1";
    data2 = "data2";
    memset(&sendmsg, 0x00, sizeof(IpcMessage)); // 重置 msg.hdr.type and msg.hdr.subtype to 0x00.
    sendmsg.internalcmd = REQ_A;//是A类请求,自定的类型,用于区分是什么类型请求
    sendmsg.data1size = data1.size();
    memcpy(&sendmsg.data1[0], data1.c_str(), data1.size());
    sendmsg.data2size = data2.size();
    memcpy(&sendmsg.data2[0], data2.c_str(), data2.size());
    
    bool ret = false;
    if (MsgSend(ChannelId, sendmsg, ((uint8_t*)&sendmsg->data2 - (uint8_t*)&sendmsg) + data2->data2size, NULL, 0) != -1) {
            //MsgSend(ConnectionId, SendBuf, SendLen, ReplyBuf, ReplyLen);
            ret = true;
     }
     
    //发送完后,关闭ChannelId
    name_close(ChannelId);
}

int main()
{
    connectservice();
    sendmsg();
}

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值