加入收藏 | 设为首页 | 会员中心 | 我要投稿 92站长网 (https://www.92zz.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Unix > 正文

Linux-C 编程 / 进程通信 / 实现基于 SysV 消息队列的文件服务器

发布时间:2022-10-15 15:30:41 所属栏目:Unix 来源:互联网
导读: 哈喽,我是老吴。最近比较懒,虽然一直在学习,但是没什么动力写文章,为了不让这个好习惯中止,就把自己最近复习到的东西总结一下分享出来,希望大佬们不要打我。
一、简介
3 种 System V

哈喽,我是老吴。最近比较懒,虽然一直在学习,但是没什么动力写文章,为了不让这个好习惯中止,就把自己最近复习到的东西总结一下分享出来,希望大佬们不要打我。

一、简介

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 手册吧。

二、实现思路

服务器和各个客户端使用单独的消息队列,服务器上的队列用来接收进入的客户端请求,相应的响应则通过各个客户端队列来发送给客户端。

unix程序员手册是什么_unix系统手册_unix程序员手册 pdf

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。

觉得文章对你有价值,不妨点个 在看和赞。

(编辑:92站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章