【问题标题】:Facing an error while sending file chunks through C++ tcp socket on Windows在 Windows 上通过 C++ tcp 套接字发送文件块时遇到错误
【发布时间】:2022-01-14 22:32:08
【问题描述】:

我正在做一个项目,该项目应该将文件作为二进制块读取并将其发送给客户端。

服务器端代码:

FILE *src = fopen("Video.mp4","rb"); // size of the video is 158 MB

int buffer = (1024*8); //chunk size of 8kb

char filebyte[buffer];

 while(!feof(src)){
    
    filebyte[0]=0; // Clearing the byte array to avoid overlapping
    
    fread(filebyte,buffer,1,src);
        
    send(acpt,filebyte,buffer,0);
   
   filebyte[0]=0; // Clearing the byte array to avoid overlapping
   
   }

fclose(src);

客户端代码


FILE *target = fopen("ReceivedVideo","wb");
int buffer = (1024*8) ; // chunk size of 8kb
char fileByte[buffer];
int stat;

while(1){
    
    fileByte[0]=0; // Clearing the byte array to avoid overlapping
    
    stat = recv(soc,fileByte,buffer,0);
    
    fwrite(fileByte,buffer,1,target);
    
    fileByte[0]=0; // Clearing the byte array to avoid overlapping
    
    if(stat<0){
        break;
    }
   
   }

fclose(target);

但是每当我发送任何视频或任何其他文件时,客户端的文件输出大小总是大于发送者实际发送的原始文件。

我发送了一个 158 MB 的视频。传输完成后,客户端生成的文件大小为 160 MB,每当我尝试在客户端目录打开接收到的视频时,视频播放器都会抛出错误。

然后我尝试发送一个 46KB 的小 .exe 文件。不得不再次面对同样的命运。客户端生成的 .exe 占用较多字节,打开时会报错。

最后我发送了一个 16kb 的 JPEG 图像文件。在客户端,这张照片占用了19kb的存储空间,与原始图像相比,我可以清楚地看到接收到的图片中有一些像素错位或模糊之类的东西,但至少这次图像查看器没有抛出错误.

我发现的问题是,虽然我像这样清除了服务器端和客户端的块存储

filebyte[0]=0; // On server side
fileByte[0]=0; // On client side

仍然在服务器或客户端接收到的字节(我真的不知道是哪一方导致了问题,但有点重叠是问题 - 根据我的说法)以某种方式重叠并损坏文件并使其大小变大。

我将完整的服务器和客户端代码放在下面。

完整的服务器端代码:

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <windows.h>
#include <winsock2.h>

#define port 8080



int main(){
    
    int buffer = (1024)*8;
    WSADATA ws;

    WSAStartup(MAKEWORD(2,2),&ws);

    SOCKET soc,acpt;

    sockaddr_in config;

    config.sin_family = AF_INET;
    config.sin_addr.s_addr = inet_addr("0.0.0.0");
    config.sin_port = htons(port);

    /*char* msg = new char[buffer];
    strcpy(msg,"Hello from Server"); */
    
    soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    
    

    bind(soc,(sockaddr*)&config,sizeof(config));

    listen(soc,1);

    acpt = accept(soc,0,0);

   FILE *src;
   
  char filename[100]="Video.mp4";
  
  char filebyte[buffer];
   
   src = fopen(filename,"rb");
   
  
   
   while(!feof(src)){
    
    filebyte[0]=0;
    
    fread(filebyte,buffer,1,src);
        
    send(acpt,filebyte,buffer,0);
   
   filebyte[0]=0;
   
   }
   
   
   
   fclose(src);
   

    std::cout << "Press Enter to exit" <<std::endl;    

//getchar();
return 0;
    
}

完整的客户端代码

#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <windows.h>
#include <winsock2.h>


#define port 8080

int main(){
    
    WSADATA ws;
    WSAStartup(MAKEWORD(2,2),&ws);

    int buffer = (1024)*8;

   // char incoming[buffer];
    
    sockaddr_in config;

    config.sin_family = AF_INET;
    config.sin_addr.s_addr = inet_addr("127.0.0.1");
    config.sin_port = htons(port);
    
    SOCKET soc;

    soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    int err = connect(soc,(sockaddr*)&config,sizeof(config));

    if(err!=0){
        std::cout << "Could not connect" <<std::endl;
    }else{
        std::cout << "Connected Successfully" <<std::endl;
    }

    FILE *target;
    
    int stat;
    char fileByte[buffer]={0};
    
   while(1){
    
    fileByte[0]=0;
    
    stat = recv(soc,fileByte,buffer,0);
    
    fwrite(fileByte,buffer,1,target);
    
    fileByte[0]=0;
    
    if(stat<0){
        break;
    }
   
   }
   
   std::cout << strlen(fileByte) << std::endl;

    fclose(target);

    
    return 0;
    
}

【问题讨论】:

  • 我没有阅读整个代码,但是我没有看到你检查fread的返回值。 fread 返回“实际读取的块数”,这正是您需要传递以发送的字节数,否则您将始终发送整个缓冲区,其中可能仍包含“旧字节”。毕竟,并不是每个文件的长度都是“缓冲区”字节的倍数。
  • 你真的在使用返回的大小值吗?我也没有阅读整个代码,但这是我遇到的常见错误,即没有使用所提供的实际返回代码/大小。相反,错误是在返回的缓冲区中粘贴不适当的空值,或者使用使用终止空值的函数/类来确定何时“停止”已经嵌入空值的数据(例如错误的 strlen 调用),或者只是假设“我要了 n 个字节,所以我会拿回 n 个字节”。
  • 除上述之外,recv 函数也没有错误检查,直到 将数据写入文件。更重要的是,您也没有检查实际收到了多少字节。您只是将完整的缓冲区长度写入文件。我真希望你不要使用标识符 buffer 来描述 size
  • 这里至少有两个明显的问题:您假设在服务器中send 发送所有内容而不是检查返回码。您在客户端中假设recv 接收所有内容,而不是检查返回码。这两个假设都是错误的。
  • 老实说,调试器是完成这项工作的最佳工具。

标签: c++ file sockets tcp winsock2


【解决方案1】:

我两次看到同样的问题,即“字节数”。

试试这个:

int32_t readBytes;
while((readBytes = fread(filebyte, 1, buffer, src)) > 0)
    send(acpt,filebyte, readBytes,0);

对于服务器端和客户端:

int stat;
while((stat = recv(soc,fileByte,buffer,0)) > 0)
    fwrite(fileByte,1, stat,target);

这段代码“想不通”,因此您可能需要相应地调整它。

解释发生了什么:fread 将返回“读取的元素数量”(有关详细信息,请参见手册页),即文件最后一个块的“小于缓冲区”。

如果您总是发送整个缓冲区,这将导致“填充输出”,最后一部分是“上一次读取期间该位置的文件中的任何内容”,这会导致您描述的问题。

同样,如果你总是写入整个缓冲区,结果文件也会以同样的方式填充。

编辑: 正如我注意到的那样,您的代码还有另一个缺陷,即您的客户端将继续等待更多数据,直到服务器关闭连接,这不会发生。

为此,您需要调用

shutdown(acpt, 2);
closesocket(acpt);

在读取/发送块之后。

或者,如果您想保持连接打开,则需要向您的协议添加其他信息。

【讨论】:

  • 阅读您的答案后,我了解了我的错误,这些代码 sn-ps 帮助我了解了我的代码中可能出现的问题,谢谢。
  • 不客气,@CoderBittu。欢迎来到 StackOverflow。 :)
【解决方案2】:

除了其他答案/cmets-

服务器代码在发送文件完成后并没有关闭 TCP 连接,所以客户端不知道何时在文件末尾终止其读取循环。

服务器应该

  • 在发送文件数据之前发送文件大小。然后客户端可以先读取大小,然后在接收到指定的字节数时停止循环。

  • 在每个缓冲区之前发送一个固定的标头,包含缓冲区大小。然后它可以在文件完成后发送一个长度为 0 的缓冲区。客户端然后可以读取每个头和缓冲区,直到它读取 0 长度缓冲区的头。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-05
    • 2011-12-03
    • 2015-01-30
    • 2020-04-28
    • 2017-03-14
    • 2020-12-09
    • 2016-01-11
    • 2019-06-24
    相关资源
    最近更新 更多