You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 12:07:09