C++ TCP传输图片损坏问题求助(附代码与警告信息)
TCP图片传输损坏及编译警告问题
问题详情
我是C初学者,正在开发文件/图片传输程序。参考TCP图片传输的实现代码后,传输后的图片出现损坏、内容缺失。使用DEV C 5.11.0.0编译时,还出现警告:[Warning] deprecated conversion from string constant to 'char*' [-Wwrite-strings]
服务端代码
int recive(SOCKET socket){ int n = 0; cout << "Reading image size" << endl; char buf[50]; int siz = 0; if ((n = recv(socket, buf, sizeof(buf), 0) <0)){ perror("recv_size()"); exit(errno); } siz = atoi(buf); cout << siz << endl; char Rbuffer[siz]; cout << "Reading image byte array" << endl; n = 0; if ((n = recv(socket, Rbuffer, siz, 0)) < 0){ perror("recv_size()"); exit(errno); } cout << "Converting byte array to image" << endl; FILE *image; image = fopen("recu.jpg", "wb"); fwrite(Rbuffer, sizeof(char), siz, image); fclose(image); cout << "done" << endl; }
客户端代码
int send(SOCKET socket){ int n = 0; int siz = 0; FILE *picture; char buf[50]; char *s=""; cout << "Getting image size" << endl; picture = fopen("test.png", "rb"); fseek(picture, 0, SEEK_END); siz = ftell(picture); cout << siz << endl; cout << "Sending picture size to the server" << endl; sprintf(buf, "%d", siz); if((n = send(socket, buf, sizeof(buf), 0)) < 0) { perror("send_size()"); exit(errno); } char Sbuf[siz]; cout << "Sending the picture as byte array" << endl; fseek(picture, 0, SEEK_END); siz = ftell(picture); fseek(picture, 0, SEEK_SET); while(!feof(picture)){ n = fread(Sbuf, sizeof(char), siz, picture); if (n > 0) { if((n = send(socket, Sbuf, siz, 0)) < 0) { perror("send_data()"); exit(errno); } } } }
现象
- 客户端输入正常PNG图片
- 服务端输出损坏图片,内容缺失
问题分析与修复
1. 图片损坏的核心原因
TCP是流式协议,recv/send不一定能一次性发送/接收所有数据,你的代码存在以下关键问题:
服务端问题
- 括号优先级错误:
(n = recv(socket, buf, sizeof(buf), 0) <0)实际是先判断recv(...) <0,再把布尔值赋值给n,导致无法正确获取接收的字节数。正确写法是(n = recv(socket, buf, sizeof(buf), 0)) <0 - 未循环接收完整数据:
recv(Rbuffer, siz, 0)可能只收到部分数据,需要循环接收直到拿到siz字节的内容 - 格式不匹配:客户端传PNG,服务端存成JPG,格式不兼容导致损坏
客户端问题
- 发送数据长度错误:
send(socket, Sbuf, siz, 0)应该发送实际读取的n字节,而不是总长度siz;且fread一次读取siz字节后,文件指针到末尾,while(!feof)会重复发送最后一次的数据 - 重复计算文件大小:已经通过
fseek和ftell获取了siz,后续重复操作完全多余 - 未处理文件打开失败:
fopen("test.png", "rb")可能失败,未做错误判断
2. 编译警告的修复
警告deprecated conversion from string constant to 'char*'是因为char *s=""把只读的字符串常量赋值给了可修改的char*,直接删除这个未使用的变量即可,或者改为const char *s=""
修复后的代码
服务端代码
#include <iostream> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sys/socket.h> using namespace std; int recive(SOCKET socket){ int n = 0; cout << "Reading image size" << endl; char buf[50]; int siz = 0; // 修复括号优先级,确保正确获取recv返回值 if ((n = recv(socket, buf, sizeof(buf)-1, 0)) < 0){ perror("recv_size()"); exit(errno); } buf[n] = '\0'; // 添加字符串结束符,避免atoi读取垃圾数据 siz = atoi(buf); cout << "Image size: " << siz << endl; // 用动态内存分配避免大图片导致栈溢出 char* Rbuffer = new char[siz]; if (!Rbuffer) { perror("malloc failed"); exit(EXIT_FAILURE); } cout << "Reading image byte array" << endl; int total_received = 0; // 循环接收直到拿到完整数据 while (total_received < siz) { n = recv(socket, Rbuffer + total_received, siz - total_received, 0); if (n < 0) { perror("recv_data()"); delete[] Rbuffer; exit(errno); } total_received += n; } cout << "Converting byte array to image" << endl; // 保存格式和输入一致,改为PNG FILE *image = fopen("recu.png", "wb"); if (!image) { perror("fopen failed"); delete[] Rbuffer; exit(errno); } fwrite(Rbuffer, sizeof(char), siz, image); fclose(image); delete[] Rbuffer; cout << "done" << endl; return 0; }
客户端代码
#include <iostream> #include <cstdlib> #include <cstring> #include <unistd.h> #include <sys/socket.h> using namespace std; int send(SOCKET socket){ int n = 0; int siz = 0; // 先判断文件是否打开成功 FILE *picture = fopen("test.png", "rb"); if (!picture) { perror("fopen failed"); exit(errno); } cout << "Getting image size" << endl; fseek(picture, 0, SEEK_END); siz = ftell(picture); if (siz < 0) { perror("ftell failed"); fclose(picture); exit(errno); } cout << "Image size: " << siz << endl; cout << "Sending picture size to the server" << endl; char buf[50]; // 用snprintf避免缓冲区溢出 snprintf(buf, sizeof(buf), "%d", siz); // 发送字符串长度+1,包含结束符 if((n = send(socket, buf, strlen(buf)+1, 0)) < 0) { perror("send_size()"); fclose(picture); exit(errno); } // 用固定大小缓冲区,避免大图片栈溢出 const int BUF_SIZE = 1024; char Sbuf[BUF_SIZE]; cout << "Sending the picture as byte array" << endl; fseek(picture, 0, SEEK_SET); while(!feof(picture)){ n = fread(Sbuf, sizeof(char), BUF_SIZE, picture); if (n <= 0) break; int total_sent = 0; // 循环发送直到当前读取的n字节全部发送完成 while (total_sent < n) { int sent = send(socket, Sbuf + total_sent, n - total_sent, 0); if (sent < 0) { perror("send_data()"); fclose(picture); exit(errno); } total_sent += sent; } } fclose(picture); cout << "done" << endl; return 0; }
额外注意事项
- 图片保存格式必须和输入一致,否则会损坏
- 避免使用变长数组(
char Rbuffer[siz]),大图片会导致栈溢出,优先用动态内存分配或固定大小缓冲区 - 所有IO操作(文件、socket)都要判断返回值,避免出错时崩溃或静默失败
内容的提问来源于stack exchange,提问作者user21806805
相关产品推荐
相关产品推荐

