| 
                        
                         哈喽,我是老吴。最近比较懒,虽然一直在学习,但是没什么动力写文章,为了不让这个好习惯中止,就把自己最近复习到的东西总结一下分享出来,希望大佬们不要打我。 
一、简介 
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。 
觉得文章对你有价值,不妨点个 在看和赞。 
                                                (编辑:泰州站长网) 
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! 
                     |