簡體   English   中英

異步TCP服務器-郵件構架建議

[英]Async TCP Server - Message Framing Advice

我在C#中有一個異步TCP套接字服務器,該服務器使用Socket的TcpListener / TcpClient包裝器制成。 我一般對網絡還很陌生,因此我不知道TCP如何處理發送的數據以及它如何不保留消息邊界。 經過一番研究,我認為我已經提出了一個可靠的解決方案,但我想知道是否有人對我有更多建議。

目前,我要發送的數據是使用protobuf(Google的數據交換庫) https://code.google.com/p/protobuf/的序列化類對象的byte []

在序列化數據之后,在發送數據之前,我決定在字節數組的開頭附加一個4字節的Int32。 我的想法是,當數據包發送到服務器時,它將解析出Int32,然后等待直到接收到該字節數,然后再對數據進行任何處理,否則只需再次調用BeginRead。

這是在我編寫數據之前運行過的代碼,它似乎可以正常工作,但是我可以接受任何性能建議:

public byte[] PackByteArrayForSending(byte[] data)
{
    // [0-3] Packet Length 
    // [3-*] original data

    // Get Int32 of the packet length
    byte[] packetLength = BitConverter.GetBytes(data.Length);
    // Allocate a new byte[] destination array the size of the original data length plus the packet length array size
    byte[] dest = new byte[packetLength.Length + data.Length];

    // Copy the packetLength array to the dest array
    Buffer.BlockCopy(packetLength, 0, dest, 0, packetLength.Length);
    // Copy the data array to the dest array
    Buffer.BlockCopy(data, 0, dest, packetLength.Length, data.Length);

    return dest;
}

我在服務器端有些卡住。 我通過使用Buffer.BlockCopy復制前4個字節,然后使用BitConverter.ToInt32來讀取我應該獲取的長度,來讀取packetLength變量。 我不確定是否應該不斷將傳入的數據讀取到特定於客戶端的Stream對象中,還是僅使用while循環。 到目前為止,這是我在服務器端擁有的代碼示例:

NetworkStream networkStream = client.NetworkStream;
int bytesRead = networkStream.EndRead(ar);

if (bytesRead == 0)
{
  Console.WriteLine("Got 0 bytes from {0}, marking as OFFLINE.", client.User.Username);
  RemoveClient(client);
}

Console.WriteLine("Received {0} bytes.", bytesRead);

// Allocate a new byte array the size of the data that needs to be deseralized.
byte[] data = new byte[bytesRead];

// Copy the byte array into the toDeserialize buffer
Buffer.BlockCopy(
  client.Buffer,
  0,
  data,
  0,
  bytesRead);

// Read the first Int32 tp get the packet length and then use the byte[4] to get the packetLength
byte[] packetLengthBytes = new byte[4];
Buffer.BlockCopy(
  data,
  0,
  packetLengthBytes,
  0,
  packetLengthBytes.Length);

int packetLength = BitConverter.ToInt32(packetLengthBytes, 0);

// Now what do you recommend?

// If not all data is received, call 
// networkStream.BeginRead(client.Buffer, 0, client.Buffer.Length, ReadCallback, client);
// and preserve the initial data in the client object

感謝您的寶貴時間和建議,我希望能進一步了解該主題。

TCP確保在一端填充到流中的字節將以相同順序掉落到另一端,並且不會丟失或重復。 僅此而已,當然不支持大於一個字節的實體。

通常,我在郵件頭的前面加上了消息的長度,類型和子類型。 通常會提供一個關聯ID,以將請求與響應進行匹配。

基本模式是獲取字節並將其附加到緩沖區。 如果緩沖區中的數據足以包含消息頭,則提取消息長度。 如果緩沖區中的數據足以包含該消息,則從緩沖區中刪除該消息並進行處理。 重復所有剩余數據,直到沒有完整的消息要處理。 根據您的應用程序,這可能是等待讀取或檢查流中是否有其他數據的關鍵。 某些應用程序可能需要繼續從單獨的線程讀取流,以避免限制發送方。

請注意,您不能假定您具有完整的消息長度字段。 處理一條消息后,您可能還剩下三個字節,並且無法提取int

根據您的消息,使用循環緩沖區可能比每次處理消息時改組剩余字節的效率更高。

暫無
暫無

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

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