C# TCP NetworkStream 缺少几个字节的数据

标签 c# filestream tcpclient file-transfer networkstream

我遇到了两个问题,在尝试了我在 stackoverflow 上读到的一些技术后,问题仍然存在。我正在尝试使用下面的代码将文件从服务器发送到客户端,但问题是该文件总是短几个字节,导致文件损坏。第二个问题是尽管实现了,但流并未关闭末尾的零长度数据包表示传输已完成而无需关闭连接。

服务器代码片段:

 /*
 * Received request from client for file, sending file to client.
 */

  //open file to send to client
  FileStream fs = new FileStream(fileLocation, FileMode.Open, FileAccess.Read);
  byte[] data = new byte[1024];
  long fileSize = fs.Length;
  long sent = 0;
  int count = 0;
  while (sent < fileSize)
  {
        count = fs.Read(data, 0, data.Length);
        netStream.Write(data, 0, count);
        sent  += count;
  }
  netStream.Write(new byte[1024], 0, 0); //send zero length byte stream to indicate end of file.
  fs.Flush();
  netStream.Flush();

客户端代码片段:

TcpClient client;
NetworkStream serverStream;


/*
*   [...] client connect
*/

//send request to server for file
byte[] dataToSend = SerializeObject(obj);
serverStream.Write(dataToSend, 0, dataToSend.Length);

//create filestream to save file
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);

//handle response from server
byte[] response = new byte[client.ReceiveBufferSize];
byte[] bufferSize = new byte[1024];
int bytesRead;
while ((bytesRead = serverStream.Read(bufferSize, 0, bufferSize.Length)) > 0 && client.ReceiveBufferSize > 0)
{
    Debug.WriteLine("Bytes read: " + bytesRead);
    fs.Write(response, 0, bytesRead);
}
fs.Close();

最佳答案

使用 UDP,您可以传输有效的空数据包,但 TCP 不允许您这样做。在应用层,TCP 协议(protocol)是字节流,所有数据包级别的内容都被抽象出来。发送零字节不会导致客户端的流级别发生任何事情。

发出文件传输结束信号可以像让服务器在发送最后一个数据 block 后关闭连接一样简单。客户端将收到最终的数据包,然后注意到套接字已关闭,这表明数据已完全传递。此方法的缺陷是 TCP 连接可能会因其他原因而关闭,从而使客户端处于一种认为自己拥有所有数据的状态,即使连接因其他原因而断开也是如此。

因此,即使您要使用“完成时关闭”方法来表示传输结束,您也需要有一种机制,允许客户端识别文件实际上已完成。

最常见的形式是在传输开始时发送一个 header block ,告诉您有关正在传输的数据的信息。这可能像 4 字节长度值一样简单,也可能是一个可变长度描述符结构,其中包括有关文件的各种元数据,例如文件的长度、名称、创建/修改时间以及您可以使用的校验和或哈希值验证收到的内容。客户端首先读取 header ,然后将流中的其余数据作为内容进行处理。

让我们采用最简单的情况,在流的开头发送 4 字节长度指示符。

服务器代码:

public void SendStream(Socket client, Stream data)
{
    // Send length of stream as first 4 bytes
    byte[] lenBytes = BitConverter.GetBytes((int)data.Length);
    client.Send(lenBytes);

    // Send stream data
    byte[] buffer = new byte[1024];
    int rc;
    data.Position = 0;
    while ((rc = data.Read(buffer, 0, 1024)) > 0)
        client.Send(buffer, rc, SocketFlags.None);
}

客户端代码:

public bool ReceiveStream(Socket server, Stream outdata)
{
    // Get length of data in stream from first 4 bytes 
    byte[] lenBytes = new byte[4];
    if (server.Receive(lenBytes) < 4)
        return false;
    long len = (long)BitConverter.ToInt32(lenBytes, 0);

    // Receive remainder of stream data
    byte[] buffer = new byte[1024];
    int rc;
    while ((rc = server.Receive(buffer)) > 0)
        outdata.Write(buffer, 0, rc);

    // Check that we received the expected amount of data
    return len == outdata.Position;
}

没有太多的错误检查等方式,以及在各个方向上阻止代码,但你明白了。

关于C# TCP NetworkStream 缺少几个字节的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23462066/

相关文章:

c# - 启动多个线程时 Silverlight 应用程序没有响应

c# - 如果文件不存在,Asp.net FileStream Create 会抛出错误

c# - ExternalException (0x80004005) GDI+ 中发生一般性错误

C++ 为什么我的示例程序不能创建输出文件?

c# - 如何向服务器发送 "hello"并回复 "hi"?

c# - .Net Socket 增强以支持 ACK 和聊天?

c# - 如何以编程方式打开 "Network Connections"窗口

c# - 具有特殊字符的正则表达式嵌套结构

c# - 使用 C# 在 XML 中选择具有特定名称的子节点

c# - 我应该怎么做才能完全关闭与mcu的tcpClient连接?