使用memcpy向共享内存传输MP4文件时出现segmentation error,接收端仍报错
共享内存传输二进制文件(MP4)的问题修复
问题根源分析
- 接收端错误获取文件大小:接收端代码错误地打开本地
sample.mp4获取文件大小,完全不符合实际传输逻辑——接收端不应依赖本地文件,必须由发送端传递文件大小信息。 - 共享内存越界访问:发送端和接收端移动
shared_buffer指针时,未检查是否超出共享内存实际容量(1024字节),文件体积超过该值时直接引发段错误。 - 发送端缓冲区读取错误:发送端
inputFile.read(buffer, sizeof(buffer))中的sizeof(buffer)获取的是指针大小(而非缓冲区的1024字节),导致每次读取数据量远小于预期。 - 无进程同步机制:发送端写完数据直接退出,接收端可能在发送端未完成写入时就开始读取,导致数据不完整或读取无效内容。
- 字符串结束标记不适用于二进制文件:MP4的字节序列可能与自定义的"END_DATA!!!"标记重合,导致传输循环提前终止。
修复后的代码
发送端代码
#include <iostream> #include <fstream> #include <unistd.h> #include <sys/shm.h> #include <sys/sem.h> #include <string.h> #include <stdlib.h> using namespace std; // 信号量P操作(等待) void sem_wait(int sem_id) { struct sembuf sb = {0, -1, 0}; semop(sem_id, &sb, 1); } // 信号量V操作(释放) void sem_post(int sem_id) { struct sembuf sb = {0, 1, 0}; semop(sem_id, &sb, 1); } int main(){ void *shared_memory; int shm_id; int sem_id; char *shared_buffer; const int SHM_SIZE = 1024; const int BUFFER_SIZE = 1024; // 创建同步用信号量 sem_id = semget((key_t)1123, 1, 0666 | IPC_CREAT); if (sem_id == -1) { cerr << "semget failed" << endl; return 1; } // 初始化信号量为0,让接收端先等待 semctl(sem_id, 0, SETVAL, 0); // 创建/获取共享内存段 shm_id = shmget((key_t)1122, SHM_SIZE, 0666 | IPC_CREAT); if (shm_id == -1) { cerr << "shmget failed" << endl; return 1; } shared_memory = shmat(shm_id, NULL, 0); if (shared_memory == (void *)-1) { cerr << "shmat failed" << endl; return 1; } shared_buffer = static_cast<char *>(shared_memory); ifstream inputFile("sample.mp4", ios::binary); if (!inputFile.is_open()) { cerr << "Error opening sample.mp4" << endl; return 1; } // 获取文件大小 inputFile.seekg(0, ios::end); streamsize fileSize = inputFile.tellg(); inputFile.seekg(0, ios::beg); if (fileSize == -1) { cerr << "Failed to get file size" << endl; return 1; } // 先将文件大小写入共享内存头部 memcpy(shared_buffer, &fileSize, sizeof(fileSize)); shared_buffer += sizeof(fileSize); size_t remaining_bytes = static_cast<size_t>(fileSize); char *buffer = new char[BUFFER_SIZE]; while (remaining_bytes > 0) { size_t read_size = min(remaining_bytes, static_cast<size_t>(BUFFER_SIZE)); inputFile.read(buffer, read_size); int bytes_read = inputFile.gcount(); if (bytes_read <= 0) break; // 确保写入不超出共享内存剩余空间 size_t write_size = min(static_cast<size_t>(bytes_read), static_cast<size_t>(SHM_SIZE - sizeof(fileSize))); memcpy(shared_buffer, buffer, write_size); remaining_bytes -= write_size; // 通知接收端读取 sem_post(sem_id); // 等待接收端读取完成 sem_wait(sem_id); // 重置共享缓冲区指针到数据起始位置 shared_buffer = static_cast<char *>(shared_memory) + sizeof(fileSize); } // 发送结束标记:将文件大小设为0 streamsize end_flag = 0; memcpy(static_cast<char *>(shared_memory), &end_flag, sizeof(end_flag)); sem_post(sem_id); inputFile.close(); delete[] buffer; shmdt(shared_memory); cout << "File transmission completed" << endl; return 0; }
接收端代码
#include <iostream> #include <fstream> #include <unistd.h> #include <sys/shm.h> #include <sys/sem.h> #include <string.h> #include <stdlib.h> using namespace std; // 信号量P操作(等待) void sem_wait(int sem_id) { struct sembuf sb = {0, -1, 0}; semop(sem_id, &sb, 1); } // 信号量V操作(释放) void sem_post(int sem_id) { struct sembuf sb = {0, 1, 0}; semop(sem_id, &sb, 1); } int main() { void *shared_memory; int shm_id; int sem_id; char *shared_buffer; const int SHM_SIZE = 1024; // 获取同步用信号量 sem_id = semget((key_t)1123, 1, 0666); if (sem_id == -1) { cerr << "semget failed" << endl; return 1; } // 获取共享内存段 shm_id = shmget((key_t)1122, SHM_SIZE, 0666); if (shm_id == -1) { cerr << "shmget failed" << endl; return 1; } shared_memory = shmat(shm_id, NULL, 0); if (shared_memory == (void *)-1) { cerr << "shmat failed" << endl; return 1; } shared_buffer = static_cast<char *>(shared_memory); ofstream outputFile("received.mp4", ios::binary); if (!outputFile.is_open()) { cerr << "Error opening received.mp4" << endl; return 1; } while (true) { // 等待发送端准备数据 sem_wait(sem_id); streamsize fileSize; memcpy(&fileSize, shared_buffer, sizeof(fileSize)); if (fileSize == 0) break; // 接收结束标记 shared_buffer += sizeof(fileSize); size_t remaining_bytes = static_cast<size_t>(fileSize); while (remaining_bytes > 0) { size_t read_size = min(remaining_bytes, static_cast<size_t>(SHM_SIZE - sizeof(fileSize))); outputFile.write(shared_buffer, read_size); remaining_bytes -= read_size; // 通知发送端继续写入 sem_post(sem_id); if (remaining_bytes > 0) { sem_wait(sem_id); shared_buffer = static_cast<char *>(shared_memory) + sizeof(fileSize); } } } cout << "File received successfully" << endl; outputFile.close(); shmdt(shared_memory); return 0; }
关键修复点说明
- 添加进程同步:用System V信号量实现收发端同步,确保接收端只读取已写入的有效数据,避免数据竞争。
- 正确传递文件大小:发送端将文件大小写入共享内存固定位置,接收端从该位置读取,不再依赖本地文件。
- 避免内存越界:每次读写都检查共享内存剩余空间,确保操作不超出容量限制。
- 修复缓冲区读取错误:发送端明确用
BUFFER_SIZE作为每次读取的最大字节数,替代错误的指针大小。 - 二进制安全结束标记:用文件大小设为0作为结束信号,避免二进制文件中巧合的字符串标记导致提前终止。
内容的提问来源于stack exchange,提问作者human
相关产品推荐
相关产品推荐

