簡體   English   中英

C#TCP NetworkStream缺少一些字節的數據

[英]C# TCP NetworkStream missing a few bytes of data

我遇到兩個問題,嘗試了一些我在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協議是字節流,所有數據包級的東西都被抽象掉了。 發送零字節將不會導致客戶端的流級別發生任何事情。

發出文件傳輸結束的信號可能很簡單,就像讓服務器在發送最后一個數據塊之后關閉連接一樣。 客戶端將收到最終數據包,然后注意套接字已關閉,這表明數據已完全傳遞。 這種方法的缺陷在於,TCP連接可能由於其他原因而關閉,從而使客戶端處於即使所有連接由於其他原因而被丟棄的情況下仍認為其擁有所有數據的狀態。

因此,即使您打算使用“完成時關閉”方法來表示傳輸結束,您也需要具有一種機制,該機制可讓客戶端識別文件實際上已完成。

最常見的形式是在傳輸開始時發送標題塊,以告知您有關正在傳輸的數據的信息。 這可能只是一個4字節長度的值,也可能是可變長度的描述符結構,其中包含有關文件的各種元數據,例如文件的長度,名稱,創建/修改時間以及可以使用的校驗和或哈希驗證收到的內容。 客戶端首先讀取標頭,然后將流中的其余數據作為內容進行處理。

讓我們以最簡單的情況為例,在流的開頭發送一個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;
}

在錯誤檢查等方面以及在各個方向上阻止代碼的方式並沒有多大幫助,但是您明白了。

沒有像在流中發送“零字節”這樣的事情。 一旦流看到您正在嘗試發送零字節,它就可以立即返回,並且將完全按照您的要求進行。

由於您使用的是TCP,因此要由您決定在客戶端和服務器之間使用已同意的協議。 例如:

  • 發送所有數據后,服務器可以關閉連接。 客戶端會將其視為“讀取”,返回零字節。

  • 服務器可以發送固定大小(可能為4個字節)的標頭,其中包括即將到來的數據的長度。 然后,客戶端可以讀取這4個字節,然后知道要等待多少個字節。

最后,您可能需要在上面的服務器代碼中使用“ netStream.Flush()”(如果您打算保持連接打開)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM