通过TCP发送/接收文件

Spr*_*dzz 1 c# tcp file send

可能重复:
TcpClient通过网络循环发送数据和接收数据,
直到TcpClient响应完全读取

我试图通过TCP将文件从服务器发送到客户端.

服务器端代码,发送文件:

NetworkStream netStream = client.GetStream();
FileStream fs = new FileStream("usb.exe",FileMode.Open, FileAccess.Read);
byte[] data = new byte[fs.Length];
fs.Read(data,0, data.Length);
fs.Flush();
fs.Close();

netStream.Write(data, 0, data.Length);
netStream.Flush();
Run Code Online (Sandbox Code Playgroud)

客户端代码,接收文件:

FileStream str = new FileStream("usb.exe", FileMode.Create, FileAccess.Write);
byte[] data = new byte[1024];

while ((dataCitit = netStream.Read(data,0, data.Length)) > 0)
    {
         Thread.Sleep(25);
         Application.DoEvents();

         str.Write(data, 0, dataCitit);
         totalbytes += dataCitit;                      
    }
str.Close();
Run Code Online (Sandbox Code Playgroud)

有人能指出我弄错了吗?

该文件有1036 kb,它只发送1032 kb然后卡住它不会在客户端输出while循环.

此外,如果我关闭服务器并快速打开它,它会发送最后一个字节,文件完全发送.(此文件打开完美)

我认为这是服务器端的问题,不是发送所有字节,而是为什么以及在哪里......

Jon*_*eet 6

那么这是您的服务器端代码中的问题:

fs.Read(data,0, data.Length);
Run Code Online (Sandbox Code Playgroud)

你忽略了返回的值Read.永远不要那样做.随着FileStream可能还好,但我个人并不反正信任它.如果您使用的是.NET 4,则无论如何都不需要这样做 - 只需使用即可Stream.CopyTo.

在客户端代码上,您最大的初始问题是您在UI线程上执行所有这些操作.这是一个可怕的想法 - 如果有网络故障,UI将冻结,因为Read呼叫阻塞.

再次,只是使用Stream.CopyTo,但在后台线程中执行.

此外,在所有这些情况下,请对流使用using语句,以便无论发生什么,都可以干净地关闭它们.

这一切都只是一般的卫生.现在,至于为什么你要挂...

...您没有关闭服务器端的网络流.因此,您永远不会在客户端到达流的末尾.如果您只需要为单个文件使用连接,那么答案很简单:只需关闭服务器端的连接即可.

但是,如果您需要为多个文件使用相同的连接,则需要更多协议 - 您需要某种方式来指示数据的结束.有三种常见的方法:

  • 在数据本身之前写入数据的长度,然后在读取端,首先读取长度,然后读取那么多字节,如果在完成之前流完成则失败.这要求您在开始编写之前知道要写入多少数据.
  • 使用您可以在阅读侧检测到的"数据结束"标记; 这是一般的痛苦,因为如果标记出现在文本本身中,它需要转义标记.
  • 第一种方法的变体,一次编写一个长度为前缀的块,然后是一个零长度的块,表示"数据结束".这非常灵活,但如果第一种方法真正适合您,显然比第一种方法更多的工作.