【发布时间】: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