哈喽,我是老吴。最近比较懒,虽然一直在学习,但是没什么动力写文章,为了不让这个好习惯中止,就把自己最近复习到的东西总结一下分享出来,希望大佬们不要打我。
一、简介
3 种 System V IPC:
System V 消息队列的特点:
System V 消息队列的优缺点:
优点:
缺点:
相关API
创建一个新消息队列或取得一个既有队列的标识符:
int msgget(key_t key, int msgflg);
向消息队列写入一条消息:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
从消息队列中读取(以及删除)一条消息并将其内容复制进 msgp 指向 的缓冲区中:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
在标识符为 msqid 的消息队列上执行控制操作:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
具体的参数说明,大佬们自行查看查阅 man 手册吧。
二、实现思路
服务器和各个客户端使用单独的消息队列,服务器上的队列用来接收进入的客户端请求,相应的响应则通过各个客户端队列来发送给客户端。

server 端:
1. 创建公用的服务器消息队列:
2. 进入 for 循环:
3. 实现 server_req(&req):
client 端:
1. 打开服务器的消息队列:
2. 创建客户端专用的消息队列:
3. 构建并通过服务器消息队列发送读文件请求:
4. 从客户端专用的消息队列先读一条消息,检查文件是否可被读:
5. 从客户端专用的消息队列中循环读取整个文件:
三、完整代码sysv_mq_file.h:
客户端发送给服务器的请求 msg:
struct request_msg {
long mtype;
int client_id;
char pathname[PATH_MAX];
};
服务器发送给客户端的响应 msg:
struct response_msg {
long mtype;
char data[RESP_MSG_SIZE];
};
// 服务器支持发送 3 种类型的消息:
#define RESP_MT_FAILURE 1 /* File couldn't be opened */
#define RESP_MT_DATA 2 /* Message contains file data */
#define RESP_MT_END 3 /* File data complete */
sysv_mq_fileserver.c:
为了便于阅读,我删除了返回值的判断:
static int server_id;
static void server_req(const struct request_msg *req)
{
struct response_msg resp;
int fd, nread;
// open file
fd = open(req->pathname, O_RDONLY);
if (fd == -1) {
resp.mtype = RESP_MT_FAILURE;
snprintf(resp.data, sizeof(resp.data), "sever couldn't open %s", req->pathname);
msgsnd(req->client_id, &resp, strlen(resp.data)+1, 0);
exit(EXIT_FAILURE);
}
// send file data msg
resp.mtype = RESP_MT_DATA;
printf("sever sending data to cliend %d\n", req->client_id);
while((nread = read(fd, resp.data, RESP_MSG_SIZE)) > 0) {
if (msgsnd(req->client_id, &resp, nread, 0) == -1) {
break;
}
}
// send end msg
resp.mtype = RESP_MT_END;
msgsnd(req->client_id, &resp, 0, 0);
}
int main(int argc, char **argv)
{
struct request_msg req;
pid_t pid;
int msglen;
server_id = msgget(SERVER_KEY, IPC_CREAT | IPC_EXCL | \
S_IRUSR | S_IWUSR | S_IWGRP);
for(;;) {
printf("server waiting, pid=%d...\n", getpid());
msglen = msgrcv(server_id, &req, REQ_MSG_SIZE, 0, 0);
pid = fork();
if(pid == 0) {
server_req(&req);
exit(0);
}
printf("\n");
}
if (msgctl(server_id, IPC_RMID, NULL) == -1) {
oops("msgctl() / IPC_RMID", errno)
}
exit(0);
}
sysv_mq_fileclient.c:
static int client_id;
int main(int argc, char **argv)
{
int server_id;
struct request_msg req;
struct response_msg resp;
int total_bytes, msg_len;
int index;
server_id = msgget(SERVER_KEY, S_IWUSR);
client_id = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR | S_IWGRP);
// any type will do
req.mtype = 1;
req.client_id = client_id;
strncpy(req.pathname, argv[1], strlen(argv[1]));
req.pathname[strlen(argv[1])] = '\0';
// send request: filename
if (msgsnd(server_id, &req, REQ_MSG_SIZE, 0) == -1)
oops("msgsnd() / filename", 5);
// get first respone
msg_len = msgrcv(client_id, &resp, RESP_MSG_SIZE, 0, 0);
if (resp.mtype == RESP_MT_FAILURE) {
printf("%s\n", resp.data);
exit(EXIT_FAILURE);
} else if (resp.mtype == RESP_MT_DATA) {
index = 0;
while(msg_len--) {
fputc(resp.data[index++], stdout);
}
}
total_bytes = msg_len;
while (resp.mtype == RESP_MT_DATA) {
msg_len = msgrcv(client_id, &resp, RESP_MSG_SIZE, 0, 0);
} else {
index = 0;
while(msg_len--) {
fputc(resp.data[index++], stdout);
}
}
total_bytes += msg_len;
}
return 0;
}
运行效果:
$ ./sysv_mq_fileserver
$ ./sysv_mq_fileclient /etc/services >/tmp/out
$ diff /etc/services /tmp/out
diff 没有任何输出,表示两个文件是相同的unix系统手册,说明文件传输成功。
四、相关参考
《Linux-UNIX 系统编程手册》 / 43、45、46章节
《UNIX 环境高级编程》 / 15.7 章节
思考技术,也思考人生
要学习技术,更要学习如何生活。
你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。
对 嵌入式系统 (Linux、OpenWrt、Android) 和 开源软件 感兴趣,关注公众号:嵌入式Hacker。
觉得文章对你有价值,不妨点个 在看和赞。
(编辑:海南站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|