在 Windows 上通过 C++ tcp 套接字发送文件块时遇到错误

Facing an error while sending file chunks through C++ tcp socket on Windows

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

服务器端代码:

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;
    
}

我看到同一个问题两次,即'number of bytes'。

试试这个:

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);

此代码为 'off the top of my head',因此您可能需要对其进行相应的调整。

解释一下发生了什么:fread 将 return 'number of elements read'(更多详细信息请参阅手册页),即 'less than buffer' 的最后一个块一个文件。

如果您总是发送整个缓冲区,这将导致 'padded output',最后一部分是 'whatever was in the file at the position during the previous read',从而导致您描述的问题。

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

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

为此,您需要调用

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

在 read/send 块之后。

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

除此之外answers/comments-

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

服务器应该

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

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