4. QNX消息传递机制
任务目标
在QNX Neutrino系统上创建两个独立进程:客户端进程通过MsgSend()发送请求,服务端进程通过MsgReceive()接收并响应,实现一次完整的请求-响应通信,同时覆盖连接建立与错误处理路径。
环境与依赖
- QNX SDP 7.0或更高版本,目标机为x86_64或ARM64架构
- 编译工具链:
qcc(QNX C编译器) - 两个源文件分别编译为独立可执行文件,运行于同一QNX节点
实现方案
服务端创建命名通道(pathname),客户端通过name_open()连接该通道。通信协议定义为:客户端发送一个整数请求,服务端返回该整数的平方值。错误处理覆盖:客户端连接失败、服务端接收失败、客户端发送失败三种路径。
完整代码
// server.c — 服务端进程
#include <stdio.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <process.h>
#define CHANNEL_NAME "square_svc"
#define REQ_TYPE 1
typedef struct {
int type;
int value;
} request_t;
typedef struct {
int result;
} response_t;
int main(void) {
int chid, rcvid;
request_t req;
response_t resp;
chid = ChannelCreate(0);
if (chid == -1) {
perror("ChannelCreate");
return 1;
}
if (name_attach(chid, CHANNEL_NAME, 0) == -1) {
perror("name_attach");
ChannelDestroy(chid);
return 1;
}
printf("Server: channel '%s' ready (chid=%d)\n", CHANNEL_NAME, chid);
while (1) {
rcvid = MsgReceive(chid, &req, sizeof(req), NULL);
if (rcvid == -1) {
perror("MsgReceive");
continue;
}
if (rcvid == 0) {
// 脉冲消息(此处忽略)
continue;
}
if (req.type != REQ_TYPE) {
resp.result = -EINVAL;
} else {
resp.result = req.value * req.value;
}
if (MsgReply(rcvid, 0, &resp, sizeof(resp)) == -1) {
perror("MsgReply");
}
}
return 0;
}
// client.c — 客户端进程
#include <stdio.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <process.h>
#define CHANNEL_NAME "square_svc"
#define REQ_TYPE 1
typedef struct {
int type;
int value;
} request_t;
typedef struct {
int result;
} response_t;
int main(int argc, char **argv) {
int coid, status;
int input = 5;
request_t req;
response_t resp;
if (argc > 1) {
input = atoi(argv[1]);
}
coid = name_open(CHANNEL_NAME, 0);
if (coid == -1) {
printf("Client: cannot connect to '%s' (errno=%d)\n",
CHANNEL_NAME, errno);
return 1;
}
req.type = REQ_TYPE;
req.value = input;
status = MsgSend(coid, &req, sizeof(req), &resp, sizeof(resp));
if (status == -1) {
printf("Client: MsgSend failed (errno=%d)\n", errno);
name_close(coid);
return 1;
}
if (resp.result < 0) {
printf("Client: server returned error %d\n", -resp.result);
} else {
printf("Client: %d^2 = %d\n", input, resp.result);
}
name_close(coid);
return 0;
}
编译与运行
# 在QNX主机环境编译
qcc -o server server.c
qcc -o client client.c
# 将两个可执行文件复制到目标机,然后在目标机shell中:
./server &
./client 7
./client 12
验证方法
- 正常路径:启动服务端后运行
./client 7,预期输出Client: 7^2 = 49;运行./client 12,预期输出Client: 12^2 = 144。 - 连接失败路径:不启动服务端直接运行
./client,预期输出包含cannot connect且errno为ENOENT(2),客户端退出码为1。 - 服务端错误响应:修改客户端
REQ_TYPE为其他值(如2)后重新编译运行,预期输出server returned error 22(EINVAL)。 - 服务端接收验证:服务端控制台打印通道就绪信息;正常通信时服务端无额外输出,客户端打印计算结果即表示
MsgReceive/MsgReply链路完整。
关键说明
name_attach()将通道ID与路径名绑定,客户端使用name_open()解析路径名并建立连接,返回连接ID(coid)。MsgSend()为阻塞发送,服务端MsgReply()返回后客户端才继续执行,天然形成同步请求-响应语义。- 错误处理覆盖三类:
name_open失败(服务端未启动)、MsgSend失败(服务端崩溃或通道关闭)、业务错误(通过响应结构体返回负的errno值)。 - 服务端为无限循环,验证结束后在目标机用
kill命令终止进程(如kill $(pidin | grep server | awk '{print $1}'))。