2

我正在开发一个 TCP 文件传输客户端-服务器程序。目前,我能够完美地发送文本文件和其他文件格式,例如在服务器端保持所有内容完好无损的 .zip。但是,当我传输 .gif 时,最终结果是一个与原始大小相同的 gif,但只有部分图像显示好像大部分字节在服务器端丢失或未正确写入。

客户端向服务器发送一个包含文件名称和大小的 1KB 标头数据包。如果准备好,服务器会以 OK 响应,然后创建一个与要发送的文件一样大的 fileBuffer。

这是一些代码来演示我的问题:

// 处理正在发送的数据的服务器端方法片段
而(真)
{
   // 旋转数据
   如果(流[0].DataAvailable)
   {
      流[0].Read(fileBuffer, 0, fileBuffer.Length);
      休息;
   }
}
// 完成接收文件,从缓冲区写入创建的文件
FileStream fs = File.Open(LOCAL_FOLDER + fileName, FileMode.CreateNew, FileAccess.Write);
fs.Write(fileBuffer, 0, fileBuffer.Length);
fs.Close();
Print("文件成功接收。");
// 处理文件发送的客户端方法片段
而(真)
{
   con.Read(ackBuffer, 0, ackBuffer.Length);
   // 等待OK响应开始发送
   if (Encoding.ASCII.GetString(ackBuffer) == "OK")
   {
      // 将文件转换为字节
      FileStream fs = new FileStream(inPath, FileMode.Open, FileAccess.Read);
      fileBuffer = 新字节[fs.Length];
      fs.Read(fileBuffer, 0, (int)fs.Length);
      fs.Close();
      con.Write(fileBuffer, 0, fileBuffer.Length);
      con.Flush();
      休息;
   }
}

我尝试了二进制编写器,而不是仅使用具有相同结果的文件流。

我是否认为成功的文件传输就像转换为字节、传输然后转换回文件名/类型一样简单?

非常感谢所有帮助/建议。

4

4 回答 4

3

它与您的形象无关.. 它与您的代码有关。

  • 如果您的图像字节丢失或未正确写入,则意味着您的文件传输代码错误,甚至会收到 .zip 文件或任何其他文件.. 它会被损坏。
  • 将字节缓冲区长度设置为文件大小是一个巨大的错误。想象一下,您要发送一个大约 1GB 的大文件 .. 那么它将需要 1GB 的 RAM .. 对于空闲传输,您应该遍历要发送的文件。

    这是一种发送/接收文件的方式,没有大小限制。

    发送文件

    using (FileStream fs = new FileStream(srcPath, FileMode.Open, FileAccess.Read))
    {
            long fileSize = fs.Length;
            long sum = 0;   //sum here is the total of sent bytes.
            int count = 0;
            data = new byte[1024];  //8Kb buffer .. you might use a smaller size also.
            while (sum < fileSize)
            {
                count = fs.Read(data, 0, data.Length);
                network.Write(data, 0, count);
                sum += count;
            }
            network.Flush();
    }
    

    接收文件

    long fileSize = // your file size that you are going to receive it.
    using (FileStream fs = new FileStream(destPath, FileMode.Create, FileAccess.Write))
    {
            int count = 0;
            long sum = 0;   //sum here is the total of received bytes.
            data = new byte[1024 * 8];  //8Kb buffer .. you might use a smaller size also.
            while (sum < fileSize)
            {
                if (network.DataAvailable)
                {
                    {
                        count = network.Read(data, 0, data.Length);
                        fs.Write(data, 0, count);
                        sum += count;
                    }
                }
            }
    }
    

    快乐编码:)

  • 于 2012-03-23T14:57:15.757 回答
    1

    正如其他人指出的那样,数据不一定都同时到达,并且您的代码每次通过循环都会覆盖缓冲区的开头。编写读取循环的更稳健的方法是读取尽可能多的可用字节并增加一个计数器以跟踪到目前为止已读取的字节数,以便您知道将它们放在缓冲区中的哪个位置。像这样的东西效果很好:

    int totalBytesRead = 0;
    int bytesRead;
    do
    {
        bytesRead = streams[0].Read(fileBuffer, totalBytesRead, fileBuffer.Length - totalBytesRead);
        totalBytesRead += bytesRead;
    } while (bytesRead != 0);
    

    Stream.Read当没有数据要读取时将返回 0。

    以这种方式做事将比一次读取一个字节执行得更好。它还为您提供了一种方法来确保您读取正确的字节数。如果totalBytesRead在循环结束时不等于您期望的字节数,则发生了一些不好的事情。

    于 2011-03-05T16:50:16.377 回答
    1

    当您通过 TCP 写入时,数据可以以多个数据包的形式到达。我认为您的早期测试恰好适合一个数据包,但是这个 gif 文件到达 2 个或更多。因此,当您调用 Read 时,您只会得到到目前为止到达的内容 - 您需要反复检查,直到您获得的字节数与标头告诉您的字节数一样多。

    在使用 TCP 进行一些工作时,我发现Beej 的网络编程指南有很大帮助。

    于 2011-03-05T15:13:55.503 回答
    0

    感谢您的输入特万佛森。我修改了我的代码并设法让它工作。我的客户端和服务器之间的同步已关闭。不过,我接受了您的建议,并用一次读取一个字节代替了 read。

    于 2011-03-05T15:15:16.970 回答