2. 进程间通信之消息传递

任务目标

本案例实现两个独立进程通过QNX消息传递机制完成一次请求-响应通信:发送进程调用MsgSend()向接收进程发送字符串消息,接收进程调用MsgReceive()接收后,使用MsgReply()回复确认信息。通过该案例掌握QNX消息传递的三个核心API的协作方式。

实现方案

创建两个C源文件:server.c(接收方)与client.c(发送方)。接收方先创建通道(ChannelCreate())并打印通道ID,发送方通过该通道ID建立连接(ConnectAttach())后发送消息。接收方收到消息后原样回复附加的确认文本。两端均使用标准输入输出打印关键状态,便于在终端直接观察通信过程。

完整代码

server.c — 消息接收与回复进程:

#include <stdio.h>
#include <string.h>
#include <sys/neutrino.h>
#include <process.h>   // 提供 getpid()

int main(void) {
    int chid, rcvid;
    char msg[128];
    char reply[128];

    // 1. 创建消息通道
    chid = ChannelCreate(0);
    if (chid == -1) {
        perror("ChannelCreate");
        return 1;
    }
    printf("Server: chid=%d, pid=%d\n", chid, getpid());
    fflush(stdout);

    // 2. 循环等待消息(本示例只处理一次后退出)
    rcvid = MsgReceive(chid, msg, sizeof(msg), NULL);
    if (rcvid == -1) {
        perror("MsgReceive");
        return 1;
    }
    printf("Server: received \"%s\" from rcvid=%d\n", msg, rcvid);

    // 3. 构造回复并发送
    snprintf(reply, sizeof(reply), "ACK from server (chid=%d)", chid);
    if (MsgReply(rcvid, 0, reply, strlen(reply) + 1) == -1) {
        perror("MsgReply");
        return 1;
    }
    printf("Server: replied \"%s\"\n", reply);

    // 4. 清理
    ChannelDestroy(chid);
    return 0;
}

client.c — 消息发送进程:

#include <stdio.h>
#include <string.h>
#include <sys/neutrino.h>
#include <process.h>

int main(int argc, char *argv[]) {
    int coid, chid, pid;
    char msg[128] = "Hello from client";
    char reply[128];

    // 从命令行获取服务端 pid 和 chid
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <server_pid> <server_chid>\n", argv[0]);
        return 1;
    }
    pid = atoi(argv[1]);
    chid = atoi(argv[2]);

    // 1. 连接到服务端通道
    coid = ConnectAttach(0, pid, chid, 0, 0);
    if (coid == -1) {
        perror("ConnectAttach");
        return 1;
    }

    // 2. 发送消息并等待回复(MsgSend 自动阻塞直到收到回复)
    if (MsgSend(coid, msg, strlen(msg) + 1, reply, sizeof(reply)) == -1) {
        perror("MsgSend");
        return 1;
    }
    printf("Client: sent \"%s\"\n", msg);
    printf("Client: got reply \"%s\"\n", reply);

    // 3. 断开连接
    ConnectDetach(coid);
    return 0;
}

编译与运行

在QNX目标机上(或QNX SDP的终端中)执行以下命令:

# 编译两个进程
qcc -o server server.c
qcc -o client client.c

# 先启动服务端(记录输出的 pid 和 chid)
./server

# 另开一个终端,用服务端输出的 pid 和 chid 启动客户端
./client <server_pid> <server_chid>

验证方法与通过标准

按以下步骤确认通信成功:

  1. 服务端终端输出包含 chid= 和 pid= 的行,记录这两个值。
  2. 客户端启动后,服务端终端应显示 Server: received "Hello from client",随后显示 Server: replied "ACK from server..."。
  3. 客户端终端应显示 Client: sent "Hello from client" 和 Client: got reply "ACK from server..."。
  4. 两端进程均正常退出(无错误信息),且服务端打印的回复内容与客户端收到的回复内容一致,即判定通过。

关键说明

  • MsgSend() 是同步阻塞调用:发送后必须等待接收方 MsgReply() 才会返回,这保证了请求-响应语义。
  • 服务端使用 MsgReceive() 的 rcvid 作为回复目标标识,该值由内核分配,不能跨进程传递使用。
  • 消息内容以字符串形式传递,长度参数需包含结尾的 '\0'(代码中通过 strlen()+1 实现)。
  • 若客户端指定的 pid 或 chid 错误,ConnectAttach() 会返回 -1,运行时报错即可定位。