2012-08-15 150 views
3

我需要將數據流式傳輸到TCP服務器,並且之前完成了它。數據應該是二進制幀格式。我用Google搜索,發現這個教程中,我想說明什麼,我需要做的:如何以二進制幀格式發送數據(TCP)

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.71).aspx

但我不知道如何在需要的,所以我可以以正確的方式發送格式創建的數據。它應該是以下格式:

Field|Offset| Type | size(octets) 
id | 0 |unsign int|  1 
name | 1 |Byte Array|  40 
grade| 41 |sign float|  8 

實施例:

Field| Data | InBytes | 
id | 133 | 133  | 
name | 247 | 247 0 | 
grade| 0 | 0  | 

請告訴我我應存儲INT的數據類型,字節數組,漂浮在和我在哪裏指定偏移量和大小以及最後如何應它被髮送到服務器(在這個例子中,他們只發送一個字節數組)。

一個C#示例如何使用上面提供的鏈接中的代碼發送像這樣的數據將不勝感激。

+0

坦率地說,我不會在這裏尋找任何特別的東西;我只讀了49個字節,然後通過偏移量手動選取它們... – 2012-08-15 13:19:46

回答

2

爲了表示要序列化的數據(和反序列化),可以使用一個結構,並設置適當的元數據,所以CLR會爲您完成剩下的工作。像其他人說的,在這裏你需要處理終點上的數據包接收。你也必須考慮接收者期望的字符集,因爲你的數據中有一個字符串字段。以下代碼是關於如何實現結構以將託管數據轉換爲二進制格式和帶註釋的示例。

// setting the layout to sequential will prevent the compiler/JIT 
// to reorder the struct fields 
// NOTE: Observe here that the Charset used is Ansi. You may need to 
// change this depending on the format expected by the receiver. 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
struct DataPacket 
{ 

    [MarshalAs(UnmanagedType.U4)] 
    public uint Id; 

    // As I understood from your question, the Name field 
    // has a prefixed size of 40 bytes. Attention here: 
    // the SizeConst actually means '40 characters', not bytes 
    // If you choose to use the Unicode charset, set SizeConst = 20 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] 
    public String Name; 

    // This will be serialized in little endian format 
    // in your question this field is 8 bytes long, which 
    // in c# corresponds to the double type. If you really mean 
    // the float type (4 bytes), change here. 
    public double Grade; 

    // Calling this method will return a byte array with the contents 
    // of the struct ready to be sent via the tcp socket. 
    public byte[] Serialize() 
    { 
     // allocate a byte array for the struct data 
     var buffer = new byte[Marshal.SizeOf(typeof(DataPacket))]; 

     // Allocate a GCHandle and get the array pointer 
     var gch = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     var pBuffer = gch.AddrOfPinnedObject(); 

     // copy data from struct to array and unpin the gc pointer 
     Marshal.StructureToPtr(this, pBuffer, false); 
     gch.Free(); 

     return buffer; 
    } 

    // this method will deserialize a byte array into the struct. 
    public void Deserialize(ref byte[] data) 
    { 
     var gch = GCHandle.Alloc(data, GCHandleType.Pinned); 
     this = (DataPacket)Marshal.PtrToStructure(gch.AddrOfPinnedObject(), typeof(DataPacket)); 
     gch.Free(); 
    } 
} 

用法:

DataPacket packet; 
packet.Id = 1234; 
packet.Name = "Marvin the paranoid robot"; 
packet.Grade = 9.2; 

// serialize 
var bytes = packet.Serialize(); 

// send via tcp 
var tcp = new TcpClient(...); 
tcp.GetStream().Write(bytes, 0, bytes.Length); 


// deserializing; 
DataPacket receivedPacket; 
receivedPacket.Deserialize(bytes); 

您已經擁有了包,現在你需要處理接收器上的數據包接收。這部分你不需要手工完成,你可以使用一些工具,比如@jgauffin說的。

2

我會採取的方法是爲我的代碼的其餘部分和正在發送的數據的物理表示之間的接口提供兩個函數。這將允許我封裝發送出TCP連接的物理數據表示,並允許它在需要時進行更改。

所以你可能會想要的是有一個TCP閱讀器類,它提供了框架組成功能和從幀的原始字節轉換爲數據結構和TCP編寫器類,它採用數據結構並創建一個原始然後將其寫入TCP連接。

我會有一個消息緩衝區,它將是一個字節數組。這是將從TCP連接發出的消息緩衝區。

接下來將是一個數據結構,其中包含數據,因爲它在系統的其餘部分中使用。

然後,我會有一個函數,說FromTcpToDataStruct (dataStruct, messageArray)從原始字節字符串轉換爲我使用的數據結構,然後FromDataStructToTcp (messageArray, dataStruct)從數據結構轉換爲原始字符串。

您有幾個問題需要考慮。首先是目標服務器上的unsigned int和float的表示形式,即Little Endian Big Endian problem。第二種是將TCP連接中的一串字節轉換爲離散幀(從TCP連接讀取字節並使用臨時緩衝區組成一個完整的原始字節幀,以便讀取提供完整的字節幀)。

1

我只是簡單地使用二進制寫入程序將數據轉換爲字節數組,然後如示例中那樣發送它。

你可能需要檢查字符串以確保它形成40個字節

 MemoryStream MS = new MemoryStream(); 
     BinaryWriter Writer = new BinaryWriter(MS); 
     Writer.Write(MyByte); 
     Writer.Write(ASCIIEncoding.UTF8.GetBytes(MyString)); 
     Writer.Write(MyDouble); 
+0

只要他確保爲名稱字段準確寫入40個字節。 – EJP 2012-08-15 22:35:22

2

你不能只是發送數據。 TCP是基於流的,這意味着您的信息可以通過一個操作(socket.Receive())或使用多個操作來接收。

解決這個問題的最簡單方法是使用包含標題(簡單長度標題就足夠了)和正文(指定信息)的數據包格式。

我不知道你是否必須自己實現一切,或者如果你可以使用網絡庫。我已經創建了一個庫來爲您處理所有套接字IO。所有你需要做的就是照顧編碼和解碼。我寫了一個簡單的二進制協議的小例子(未完成)。

的編碼器/解碼器的類可以在這裏找到:https://github.com/jgauffin/griffin.networking/tree/master/Source/Protocols/SimpleBinary/Griffin.Networking.Protocol.SimpleBinary/Handlers

甲Griffin.Networking介紹:http://blog.gauffin.org/2012/05/griffin-networking-a-somewhat-performant-networking-library-for-net/

+0

只要他不發送任何其他格式,他確實可以發送該格式。 – EJP 2012-08-15 22:34:07