C#TCP文件传输 - 图像半传输

时间:2011-03-05 14:30:43

标签: c# file tcp transfer

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

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

以下是一些用于演示我的问题的代码:

// Serverside method snippet dealing with data being sent
while (true)
{
   // Spin the data in
   if (streams[0].DataAvailable)
   {
      streams[0].Read(fileBuffer, 0, fileBuffer.Length);
      break;
   }
}
// Finished receiving file, write from buffer to created file
FileStream fs = File.Open(LOCAL_FOLDER + fileName, FileMode.CreateNew, FileAccess.Write);
fs.Write(fileBuffer, 0, fileBuffer.Length);
fs.Close();
Print("File successfully received.");
// Clientside method snippet dealing with a file send
while(true)
{
   con.Read(ackBuffer, 0, ackBuffer.Length);
   // Wait for OK response to start sending
   if (Encoding.ASCII.GetString(ackBuffer) == "OK")
   {
      // Convert file to bytes
      FileStream fs = new FileStream(inPath, FileMode.Open, FileAccess.Read);
      fileBuffer = new byte[fs.Length];
      fs.Read(fileBuffer, 0, (int)fs.Length);
      fs.Close();
      con.Write(fileBuffer, 0, fileBuffer.Length);
      con.Flush();
      break;
   }
}

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

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

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

4 个答案:

答案 0 :(得分:1)

当您通过TCP写入时,数据可以到达多个数据包。我认为你的早期测试碰巧适合一个数据包,但这个gif文件到达2个或更多。因此,当您调用Read时,您将只获得到目前为止已到达的内容 - 您需要反复检查,直到您获得了与标题告诉您期望的字节数相同的字节数。

在使用TCP进行一些工作时,我发现Beej's guide to network programming是一个很大的帮助。

答案 1 :(得分:1)

它不是关于你的形象..这是关于你的代码。

  • 如果您的图像字节丢失或未正确写入,则意味着您的文件传输代码错误,甚至会收到.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;
                    }
                }
            }
    }
    

    快乐的编码:)

  • 答案 2 :(得分:0)

    感谢您输入Tvanfosson。我修改了我的代码,并设法让它工作。我的客户端和服务器之间的同步性已关闭。我接受了你的建议,并将读取替换为一次读取一个字节。

    答案 3 :(得分:0)

    正如其他人所指出的那样,数据不一定全部到达,并且每次循环时你的代码都会覆盖缓冲区的开头。编写读取循环的更健壮的方法是读取尽可能多的字节数,然后递增计数器以跟踪到目前为止已读取的字节数,以便您知道将它们放在缓冲区中的位置。这样的事情效果很好:

    int totalBytesRead = 0;
    int bytesRead;
    do
    {
        bytesRead = streams[0].Read(fileBuffer, totalBytesRead, fileBuffer.Length - totalBytesRead);
        totalBytesRead += bytesRead;
    } while (bytesRead != 0);
    
    当没有数据可供阅读时,

    Stream.Read将返回0。

    以这种方式执行操作比一次读取一个字节要好。它还为您提供了一种确保读取正确字节数的方法。如果totalBytesRead不等于循环结束时预期的字节数,那么就会发生一些不好的事情。