使用共享内存传输文件
功能已经完成了,传小文本没有问题,一旦传输超过自定义的共享内存大小的文件就会出现数据不全的问题。例如传输一张图片,接收到的文件大小比原文件小很多。
代码:
头文件: ipcs_shm.h
#ifndef __IPCS_SHM_H__ #define __IPCS_SHM_H__ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/types.h> #include <sys/shm.h> #include <unistd.h> #define PATHNAME "." // 路径名 #define PROID 10 // 低8位整数 #define SZ_SHM 1024 #endif
发送文件:send_process.c
#include "ipcs_shm.h" int sendfile(void *addr, char *argv[]) { FILE *fp_read = NULL; size_t rbytes; fp_read = fopen(argv[1], "r"); if (fp_read == NULL) { perror("Error fread fopen() "); exit(EXIT_FAILURE); } while (1)// 循环读取文件数据到共享内存 { rbytes = fread(addr, sizeof(char), SZ_SHM, fp_read); if (rbytes > 0) { while (strlen(addr) != 0)// 如果共享内存数据未被取走,则读操作暂停1秒 { fprintf(stdout, "wirte process busy...\n"); fflush(NULL); sleep(1); } } else break; } fclose(fp_read); return 0; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage : %s <dest_pathname> .\n", argv[0]); return -1; } key_t key; int shmid; void *addr = NULL; int ret = 0; key = ftok(PATHNAME, PROID); // 申请消息队列的msqid if (key == -1) { perror("fotk(): "); exit(EXIT_FAILURE); } shmid = shmget(key, SZ_SHM, IPC_CREAT | 0666); // 创建共享内存id if (shmid == -1) { perror("shmid(): "); exit(EXIT_FAILURE); } printf("shmid = %d\n", shmid); addr = shmat(shmid, NULL, 0); // 映射共享内存 if (addr == (void *)-1) { perror("[ERROR] shmat(): "); exit(EXIT_FAILURE); } sendfile(addr, argv); shmdt(addr); // 解除映射 fprintf(stdout, "read file over.\n"); fflush(NULL); return 0; }
接收文件:rcv_process.c
#include "ipcs_shm.h" int waitcount = 0; void rsvfile(void *addr, char *argv[]) { FILE *fp_write = NULL; size_t wbytes; fp_write = fopen(argv[1], "w"); if (NULL == fp_write) { perror("Error write fopen() "); exit(EXIT_FAILURE); } // 循环写操作 while (1) { // 将共享内存的数据写入到文件 fp_write中 wbytes = fwrite(addr, sizeof(char), strlen(addr), fp_write); if (0 > wbytes) { perror("[ERROR] fwrite() "); exit(EXIT_FAILURE); } // 连续3次没有读取到数据则结束 写操作 while (0 == wbytes) { if (3 == ++waitcount) { fprintf(stdout, "wirte process close.\n"); fflush(NULL); fclose(fp_write); return ; } fprintf(stdout, "wirte waiting...\n"); fflush(NULL); sleep(1); wbytes = fwrite(addr, sizeof(char), strlen(addr), fp_write); } // 每次写操作完成后,清空共享内存 memset(addr, 0, SZ_SHM); waitcount = 0; } } int main(int argc, char *argv[]) { if (2 != argc) { fprintf(stderr, "Usage : %s <dest_pathname> .\n", argv[0]); return -1; } key_t key; int shmid; void *addr = NULL; int ret = 0; key = ftok(PATHNAME, PROID); // 申请消息队列的msqid if (key == -1) { perror("fotk(): "); exit(EXIT_FAILURE); } shmid = shmget(key, SZ_SHM, IPC_CREAT | 0666); // 创建共享内存id if (shmid == -1) { perror("shmid(): "); exit(EXIT_FAILURE); } printf("shmid = %d\n", shmid); addr = shmat(shmid, NULL, 0); // 映射共享内存 if ((void *)-1 == addr) { perror("[ERROR] shmat(): "); exit(EXIT_FAILURE); } rsvfile(addr, argv); // 文件接收 shmdt(addr); // 解除映射 ret = shmctl(shmid, IPC_RMID, NULL); // 销毁共享内存 if (ret == -1) { perror("[ERROR] shmctl(): "); exit(EXIT_FAILURE); } return 0; }
27
收起
正在回答
1回答
主要问题在于strlen 函数计算共享内存中二进制数据的长度是不准确的,具体解决方案如下:
(a) . 在申请共享内存空间时,需要多申请4个字节用于共享每次写入共享内存的数据长度
(b). 在从共享内存中读取数据时,需要先从共享内存前4个字节读取写入的长度,在根据实际写入到共享内存的长度来读取数据
下面是经过修改的代码,目前测试了图片大小相同,具体内容还需要你进一步测试一下
#include "ipcs_shm.h" int waitcount = 0; void rsvfile(void *addr, char *argv[]) { FILE *fp_write = NULL; size_t wbytes,length = 0; fp_write = fopen(argv[1], "w"); if (NULL == fp_write) { perror("Error write fopen() "); exit(EXIT_FAILURE); } // 循环写操作 while (1) { memcpy(&length,addr,4);// 从共享内存中获取长度 // 将共享内存的数据写入到文件 fp_write中 wbytes = fwrite(addr + 4, sizeof(char),length, fp_write);//根据写入的长度将数据写入到共享内存中 if (0 > wbytes) { perror("[ERROR] fwrite() "); exit(EXIT_FAILURE); } memset(addr,'\0',4);//读取完成后将数据清零 // 连续3次没有读取到数据则结束 写操作 while (0 == wbytes) { if (3 == ++waitcount) { fprintf(stdout, "wirte process close.\n"); fflush(NULL); fclose(fp_write); return ; } fprintf(stdout, "wirte waiting...\n"); fflush(NULL); sleep(1); memcpy(&length,addr,4);//获取长度 wbytes = fwrite(addr, sizeof(char), length, fp_write); memset(addr,'\0',4);//将长度清零 } // 每次写操作完成后,清空共享内存 memset(addr, 0, SZ_SHM + 4); waitcount = 0; } } int main(int argc, char *argv[]) { if (2 != argc) { fprintf(stderr, "Usage : %s <dest_pathname> .\n", argv[0]); return -1; } key_t key; int shmid; void *addr = NULL; int ret = 0; key = ftok(PATHNAME, PROID); if (key == -1) { perror("fotk(): "); exit(EXIT_FAILURE); } shmid = shmget(key, SZ_SHM + 4, IPC_CREAT | 0666); // 创建共享内存id if (shmid == -1) { perror("shmid(): "); exit(EXIT_FAILURE); } printf("shmid = %d\n", shmid); addr = shmat(shmid, NULL, 0); // 映射共享内存 if ((void *)-1 == addr) { perror("[ERROR] shmat(): "); exit(EXIT_FAILURE); } rsvfile(addr, argv); // 文件接收 shmdt(addr); // 解除映射 ret = shmctl(shmid, IPC_RMID, NULL); // 销毁共享内存 if (ret == -1) { perror("[ERROR] shmctl(): "); exit(EXIT_FAILURE); } return 0; }
#include "ipcs_shm.h" int sendfile(void *addr, char *argv[]) { FILE *fp_read = NULL; size_t rbytes; fp_read = fopen(argv[1], "r"); if (fp_read == NULL) { perror("Error fread fopen() "); exit(EXIT_FAILURE); } while (1)// 循环读取文件数据到共享内存 { rbytes = fread(addr + 4, sizeof(char), SZ_SHM, fp_read);//将文件的数据写入到共享内存中 if (rbytes > 0) { memcpy(addr,&rbytes,4);//将长度写入到共享内存的前4个字节 memcpy(&rbytes,addr,4);//重新读取长度 while(rbytes != 0) //while (strlen(addr) != 0)// 如果共享内存数据未被取走,则读操作暂停1秒 { memcpy(&rbytes,addr,4);//每次重新读取长度 fprintf(stdout, "wirte process busy...\n"); fflush(NULL); sleep(1); } } else break; } fclose(fp_read); return 0; } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage : %s <dest_pathname> .\n", argv[0]); return -1; } key_t key; int shmid; void *addr = NULL; int ret = 0; key = ftok(PATHNAME, PROID); // 申请消息队列的msqid if (key == -1) { perror("fotk(): "); exit(EXIT_FAILURE); } shmid = shmget(key, SZ_SHM + 4, IPC_CREAT | 0666); // 创建共享内存id if (shmid == -1) { perror("shmid(): "); exit(EXIT_FAILURE); } printf("shmid = %d\n", shmid); addr = shmat(shmid, NULL, 0); // 映射共享内存 if (addr == (void *)-1) { perror("[ERROR] shmat(): "); exit(EXIT_FAILURE); } sendfile(addr, argv); shmdt(addr); // 解除映射 fprintf(stdout, "read file over.\n"); fflush(NULL); }
下面是我写的参考答案
include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/sem.h> #include <signal.h> #define PATHNAME "." struct file_mem{ int magic_no;/*数据块的编号*/ int size;/*数据块的大小*/ char buffer[1024];/*数据块缓冲区*/ }; /*共享内存创建标志*/ int g_shm_created = 1; /* *获取共享内存key * * */ key_t getkey(void) { return ftok(PATHNAME,1); } /* *创建共享内存 * *返回struct file_mem 指针 * */ struct file_mem *open_shm(void) { key_t key; int shm; key = getkey(); shm = shmget(key,sizeof(struct file_mem),0666); if (shm < 0){ shm = shmget(key,sizeof(struct file_mem),0666|IPC_CREAT); if (shm < 0){ return NULL; } /*创建标志设置为共享内存的id*/ g_shm_created = shm; } /*进程映射到共享内存中*/ return (struct file_mem *)shmat(shm,NULL,0); } /* *解除映射,并删除共享内存 * */ void close_shm(struct file_mem *p) { shmdt(p); if (g_shm_created) shmctl(g_shm_created,IPC_RMID,NULL); } /*等待文件传输 * * */ void wait_for_file(void) { struct file_mem *p = open_shm(); if (p != NULL){ int times = 0; /*last_no 记录最后一次数据块的编号*/ int last_no = p->magic_no; /*当last_no 等于数据块编号时,则表示没有开始传输*/ while(last_no == p->magic_no) { usleep(100000); } printf("[Server is online!]\n"); /*当last_no 与p->magic_no编号不一致时,赋值给last_no*/ last_no = p->magic_no; while(1){ /*当last_no与数据块编号不相等时,更新last_no*/ if (last_no != p->magic_no){ times = 0; last_no = p->magic_no; }else{ times++; if (times > 1000){ printf("[Server is down.]\n"); break; } } /*当size >0 则表示有数据*/ if (p->size > 0){ p->buffer[p->size] = 0;/*将数据块尾添加'\0'*/ printf(p->buffer);/*打印到stdout*/ fflush(stdout); p->size = 0;/*将数据块的大小置为0,等待下一个数据块*/ }else{ usleep(1000);/*没有数据则睡眠*/ } } close_shm(p); } } int main(int argc,char *argv[]) { struct file_mem *p; FILE *fp; size_t reads; if (argc < 2){ wait_for_file(); exit(0); } if ((fp = fopen(argv[1],"r+")) == NULL){ perror("fopen failed.\n"); exit(0); } p = open_shm();/*创建共享内存并映射到共享内存上*/ if (p != NULL){ p->size = 0;/*将size设置为0*/ while(!feof(fp)){ p->magic_no++;/*数据块编号加1*/ /*将size = 0时,在将数据写入到共享内存中*/ if (p->size == 0){ /*数据缓冲区添加'\0'*/ p->buffer[sizeof(p->buffer) -1] = 0; /*从文件中读取数据到buffer中,并留一个位置给'\0'*/ reads = fread(p->buffer,1,sizeof(p->buffer) - 1,fp); if(reads > 0){ p->size = reads;/*读取成功,则更新size的值*/ } } } printf("send over.\n"); close_shm(p); } fclose(fp); }
物联网/嵌入式工程师
- 参与学习 394 人
- 提交作业 22954 份
- 解答问题 1163 个
行业热门,政策风口,人才缺口极大,现在入场时机正好! 上千人检验,数轮迭代的硬核知识体系,软硬件通吃 保姆式教学+简历指导+1V1模拟面试+3次内推,助力轻松就业!
了解课程
恭喜解决一个难题,获得1积分~
来为老师/同学的回答评分吧
0 星