【问题标题】:files are damaged after downloading from an ftp server从 ftp 服务器下载后文件损坏
【发布时间】:2018-03-29 19:47:50
【问题描述】:

这是我所拥有的:

    send(serverSocket, "RETR user.png\r\n", 15, 0);

    int iResult;
    int size = 0;

    do {

        iResult = recv(dataSocket, buffer, 30000, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);
            size = size + iResult;
        }
        else if (iResult == 0)
            printf("Connection closed\n");
        else
            printf("recv failed: %d\n", WSAGetLastError());

    } while (iResult > 0);

    cout << "the full size: " << size << endl;

    ofstream fout("test.png", ios_base::binary);
    fout.write(buffer, size);
    fout.close();

在这里,我将一个文件下载到一个缓冲区中,然后创建一个相同类型的文件并将这个缓冲区写入其中。问题是我无法打开此文件,因为它已损坏。

我尝试使用十六进制编辑器打开此文件,它与好的编辑器不同,但差别不大。只有一些额外的字节,整个数据因此而移动了一点。 它们来自哪里以及如何摆脱它们?我的程序出了什么问题?

hexidemical representation (test.png是我下载的,user.png是好的,我用浏览器下载的)。我标记了 2 个额外的值,但还有更多。我希望弄清楚交易是什么有用的。提前致谢。

【问题讨论】:

  • 您是否发送了使用二进制模式的说明?
  • 为什么人们坚持只发送 sn-ps 代码而不是完整的、可验证的示例(完整,显示失败的案例),而不是如此不精确?
  • 您需要阅读更多的 FTP 协议,因为它可能会即时将所有新行转换为 CRLF 对。这发生在所有 POSIX 系统中(可能在 Windows 的 FTP 服务器的某些实现上,纯二进制文件以 TEXT 模式传输),您需要在传输之前设置 BINARY 模式。

标签: c++ sockets ftp


【解决方案1】:

这是你的问题:

iResult = recv(dataSocket, buffer, 30000, 0);

每次循环都会覆盖“缓冲区”的开头。您希望追加到已写入数据的末尾,而不是覆盖它。

这就是你想要的:

iResult = recv(dataSocket, buffer+size, 30000-size, 0);

我假设缓冲区是一个字符数组(或无符号字符,或任何具有 sizeof(1) 的字符)。如果没有,那么这个:

char* ptr = (char*)buffer;
iResult = recv(dataSocket, ptr+size, 30000-size, 0);

另一个错误是因为您在服务器上处于 ASCII 模式。所有这些 0A 字节都被解释为 Unix 换行符,并且它很乐意在每个字节之前插入 Windows 回车符 0D 字符。我不确定这是否是因为服务器以这种方式发送它,或者您保存的 I/O 调用不是二进制模式。但是每当我进行 FTP 传输时,我总是确保在命令提示符下键入“二进制”,这样就不会应用 ASCII 帮助。

查看FTP命令列表,你可能需要发送“TYPE”命令来强制二进制模式。如果不是这样,那很可能是 ofstream 是......

【讨论】:

  • ..或者,每次都从缓冲区中写出字节,而不是等到所有数据都收到。
  • 总是只有一次迭代,我可以删除这个循环并输入:recv(dataSocket, buffer, 30000, 0) 仅此而已,结果将是相同的(文件大小约为 15 kb) .我也试过你的代码,结果还是一样 - 我无法打开文件,十六进制表示中仍然有那些奇怪的值。
  • @ShadeWe - 通过该循环并不总是“一次迭代”。 TCP 分段、IP 分段、网络中的打嗝和一般互联网行为会影响 TCP 数据包的到达。如果整个 15KB 在调用 recv 之前设法到达网络流,您将在一次调用中获取所有数据。否则,这是一个竞争条件,您的代码将循环多次。这是否解释了您看到的损坏的文件内容是另一回事,但这仍然是您代码中的一个真正的错误。
  • @ShadeWe - 现在我查看了您的十六进制转储,很明显这是一个 ASCII 转换问题。请参阅上面的我的 cmets。
  • 两件事 我说的都是问题所在。进行两个修复。很高兴我能提供帮助。
最近更新 更多