在通过套接字向服务器发送文件数据时,我得到了一些垃圾值?为什么

I am getting some garbage value while sending file data through socket to server? Why?

本文关键字:为什么 套接字 服务器 数据 文件      更新时间:2023-10-16

我正在通过C++中的套接字建立客户端-服务器连接。我成功地连接了它们,但在发送文件大小和文件数据时,我在服务器中也收到了一些垃圾值。

我首先从客户端通过发送系统调用发送文件大小,然后将文件缓冲区发送到服务器。

我在服务器上有recv系统调用,它成功地接收了Filesize,但在获取一些字节后的文件数据时,我得到了垃圾。

客户代码

File = fopen("index.jpg", "rb");
if (!File)
{
MessageBox(L"Error while readaing the file");
}
fseek(File, 0, SEEK_END);
Size = ftell(File);
fseek(File, 0, SEEK_SET);
char* Buffer = new char[Size];
fread(Buffer, Size, 1, File);

char cSize[MAX_PATH];
sprintf(cSize, "%lu", Size);

send(Socket, cSize, MAX_PATH, 0); // File size
send(Socket, Buffer, Size, 0); // File Binary

服务器代码

unsigned long Size;
char *Filesize = new char[1024];
if (recv(Sub, Filesize, 1024, 0)) // File size
{
Size = strtoul(Filesize, NULL, 0);  //getting filesize
}

Buffer = new char[Size];
int reader = recv(Sub, Buffer, Size, 0);
Buffer[Size] = '';
if (reader == -1) // File Binary
{
MessageBox(L"Perror Recv");
}
else if (reader == 0)
{
MessageBox(L"Connection is Closed");
}
else
{
FILE *File;
File = fopen("test.jpg", "wb");
fwrite((const char*)Buffer, 1, Size, File);  
MessageBox(L"DATA Received");
fclose(File);
}

一个问题是您没有正确处理recv()的返回值。例如:

if (recv(Sub, Filesize, 1024, 0)) // File size

当上面引用的函数返回时,它已经向Filesize写入了一些字节数(大于0,小于1025)。有多少您的程序不知道,因为您没有将返回值存储到变量中以查找(相反,您只是检查它是否为非零,然后丢弃该值)。因此,Filesize很可能不仅包含文件大小值,还包含文件数据的某些部分。。。这就是为什么你的文件的那部分数据以后不会在你的程序中写入磁盘的原因。

这里有一个类似的问题:

int reader = recv(Sub, Buffer, Size, 0);

您可以检查reader,看看它是-1还是0(这很好),但在最后一种情况下,当Buffer包含reader字节而不是Size字节时,您只从阵列中取出Size字节中的fwrite()(并且reader可以具有1Size之间的任何值,这取决于TCP堆栈决定在特定的recv()调用中传递给您的字节数。

还有一个问题是,您发送的MAX_PATH字节是文件大小,但收到的却是(最多)1024字节的文件大小。MAX_PATH等于1024吗?如果没有,那么即使recv()确实填充了所有1024个字节,您的发送方和接收方仍然会彼此不同步,因为多余的字节会出现在未来的recv()调用中,或者(或者)您会从随后的send()调用中获得字节,并将其放入FileSize缓冲区。

所以这就是直接的问题——我认为根本的问题是,你对TCP网络的工作方式做出了一些不正确的假设。特别是:

  • 不能保证send()recv()调用之间的一一对应。(TCP是一种字节流协议,不进行任何数据成帧)

  • 您不能依赖于通过对recv()的单个调用传递对send()的单个调用中的N字节数据。send()的字节将按顺序传递,但无法保证需要多少次对recv()的调用才能全部接收,也无法保证任何对recv()的给定调用将写入接收缓冲区的字节数。

  • 你不能依赖recv()来填满你传递给它的整个缓冲区。recv()会写尽可能少或尽可能多的字节,无论每次recv()调用有多少字节,都取决于你的代码来正确处理它。

在实践中,这意味着您需要在循环中调用recv(),并仔细跟踪每次recv()调用的返回值,这样您就可以始终准确地知道到目前为止收到了多少字节,从而知道下一个recv()调用应该在缓冲区内的何处开始写入。

您没有处理send和recv函数的响应,请确保收集这些响应,因为发送和接收的字节数很多,并且需要进一步使用它们。