1

可能重复:
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();

客户端代码,接收文件:

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();

有人能指出我哪里弄错了吗?

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

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

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

4

1 回答 1

6

好吧,这是您的服务器端代码中的一个问题:

fs.Read(data,0, data.Length);

您忽略了Read. 永远不要那样做。和FileStream你一起可能没问题,但我个人无论如何都不会相信它。如果您使用的是 .NET 4,则无论如何都不需要这样做 - 只需使用Stream.CopyTo.

在客户端代码上,您最大的初始问题是您在 UI 线程上执行所有这些操作。这是一个糟糕的主意——如果出现网络故障,UI 将冻结,因为Read呼叫被阻塞。

同样,只需使用Stream.CopyTo,但在后台线程中执行。

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

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

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

但是,如果您需要对多个文件使用相同的连接,那么您需要更多的协议——您需要某种方式来指示数据的结束。有三种常见的方法来做到这一点:

  • 在数据本身之前写入数据的长度,然后在读取端,首先读取长度,然后读取那么多字节,如果流在您完成之前完成则失败。这要求您在开始写入之前知道要写入多少数据。
  • 使用可以在读取端检测到的“数据结束”标记;这通常是一种痛苦,因为如果它出现在文本本身中,它需要转义标记。
  • 第一种方法的变体,您一次写入一个以长度为前缀的块,然后写入一个零长度块以指示“数据结束”。这是非常灵活的,但如果第一种方法确实适合您,那么显然比第一种方法工作更多。
于 2012-11-23T07:31:38.190 回答