c++ - 通过套接字将文件数据发送到服务器时,我得到了一些垃圾值?为什么?
问题描述
我正在通过 C++ 中的套接字建立客户端服务器连接。我成功地连接了它们,但是在发送文件大小和文件数据时,我也在我的服务器中收到了一些垃圾值。
我首先通过客户端的发送系统调用发送文件大小,然后将文件缓冲区发送到服务器。
我在成功接收文件大小的服务器中有recv系统调用,但是在一些字节后获取文件数据时,我得到了垃圾。
客户代码
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] = '\0';
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
...当上面引用的函数返回时,它已将一些字节数(大于 0,小于 1025)写入Filesize
. 多少?您的程序不知道,因为您没有将返回值存储到变量中以查找(而是您只检查它是否非零,然后丢弃该值)。因此,很可能不仅Filesize
包含您的文件大小值,还包含文件数据的某些部分......这就是为什么您的程序稍后不会将这部分文件数据写入磁盘的原因。
这里有一个类似的问题:
int reader = recv(Sub, Buffer, Size, 0);
您检查reader
它是否是-1
or 0
(这很好),但在您的最后一种情况下,您只是从数组中fwrite()
取出字节,当包含字节而不是字节(并且可以具有和之间的任何值,具体取决于 TCP 堆栈的字节数决定在那个特定的电话中交付给您。Size
Buffer
reader
Size
reader
1
Size
recv()
另一个问题是您发送MAX_PATH
文件大小的字节,但您收到(最多)1024 个字节的文件大小。MAX_PATH
等于1024 ?如果没有,那么即使recv()
确实填写了所有 1024 个字节,您的发送方和接收方仍然会彼此不同步,因为多余的字节会出现在未来的recv()
调用中,或者(或者)您会从后续send()
调用中获取字节放入您的 FileSize 缓冲区。
这就是直接的问题——我认为根本的问题是您对 TCP 网络的工作方式做出了一些不正确的假设。尤其:
send()
不保证和recv()
调用之间的一一对应关系。(TCP 是一种字节流协议,不做任何数据分帧)您不能依赖从单个调用到
send()
通过单个调用传递的N 字节数据recv()
。send()
您将按顺序传递的字节,但不能保证需要多少次调用才能recv()
全部接收它们,也不保证任何给定调用recv()
将写入您的接收缓冲区的字节数。您不能依靠
recv()
填满您传递给它的整个缓冲区。recv()
将写入尽可能少或尽可能多的字节,并且无论每次recv()
调用获得多少字节,都取决于您的代码是否正确处理它。
在实践中,这意味着您需要recv()
循环调用,并仔细跟踪每次recv()
调用的返回值,因此您始终知道到目前为止您收到了多少字节,因此下一次recv()
调用在缓冲区内的位置应该开始写作。
推荐阅读
- jenkins - Jenkins“创建用户”在 Mac OS X High Sierra 上失败
- jmeter - Jmeter - 如何继续尝试相同的请求直到它成功
- android - 无法将本地 html 文件加载到 Android 应用程序中的默认设备浏览器中
- javascript - 错误消息“Uncaught TypeError: undefined is not a function”(数据表)
- c# - Web api:[FromBody] 总是返回计数为 0 的列表
- javascript - 破坏可选函数参数
- php - 将数据从 mySQL 拉到文本文档 php
- c# - Mass Transit RabbitMQ 请求/响应 - 响应移动到 _skipped 队列
- dart - Flutter / dart异常地理定位错误
- azure - 是否可以在本地运行 Azure Service Fabric Mesh?