2010-02-23 70 views
9

我有一個客戶端服務器應用程序,其中服務器和客戶端需要通過網絡發送和接收自定義類的對象。我正在使用TcpClient類來傳輸數據。我在發送端序列化對象,並將產生的字節流發送給接收者。但在接收器,當我嘗試反序列化收到的字節,它會拋出異常序列化和細節是:使用C#中的Tcpclient類發送和接收自定義對象

輸入流不是有效的 二進制格式。起始內容 (以字節爲單位)爲: 0D-0A-00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00 ...

序列化對象我的服務器代碼:

byte[] userDataBytes; 
MemoryStream ms = new MemoryStream(); 
BinaryFormatter bf1 = new BinaryFormatter(); 
bf1.Serialize(ms, new DataMessage()); 
userDataBytes = ms.ToArray(); 
netStream.Write(userDataBytes, 0, userDataBytes.Length); 

客戶端代碼解串是:

readNetStream.Read(readMsgBytes, 0, (int)tcpServer.ReceiveBufferSize); 
MemoryStream ms = new MemoryStream(readMsgBytes); 
BinaryFormatter bf1 = new BinaryFormatter(); 
ms.Position = 0; 
object rawObj = bf1.Deserialize(ms); 
DataMessage msgObj = (DataMessage)rawObj; 

請幫我解決這個問題,可能提出的任何其他方法傳輸自定義類acr的對象在C#中使用TcpClient的oss網絡。

謝謝, Rakesh。

回答

11

當接收上客戶端,你不知道你想讀多少數據。 您只依賴於ReceiveBufferSize,而您的數據可能會更大或更小 或更小。

我覺得這裏最好的辦法是發送4個字節,告訴你的客戶對輸入數據的長度:

byte[] userDataLen = BitConverter.GetBytes((Int32)userDataBytes.Length); 
netStream.Write(userDataLen, 0, 4); 
netStream.Write(userDataBytes, 0, userDataBytes.Length); 

,並在recieving結束時,你第一次讀數據的長度,然後讀取 確切數額數據的。

byte[] readMsgLen = new byte[4]; 
readNetStream.Read(readMsgLen, 0, 4); 

int dataLen = BitConverter.ToInt32(readMsgLen); 
byte[] readMsgData = new byte[dataLen]; 
readNetStream.Read(readMsgData, 0, dataLen); 

逸岸,我才意識到,那你可能需要做更多一點,以確保你讀的所有數據(只是一個想法,因爲我還沒有嘗試過,但只是櫃面你遇到問題你可以再次嘗試這個)。

NetworkStream.Read()方法返回一個數字,表示它讀取的數據量。傳入的數據可能大於RecieveBuffer。在這種情況下,您必須循環,直到您讀取所有數據。你必須做這樣的事情:

SafeRead(byte[] userData, int len) 
{ 
    int dataRead = 0; 
    do 
    {  
     dataRead += readNetStream.Read(readMsgData, dataRead, len - dataRead); 

    } while(dataRead < len); 
} 
7

看一看this code。它採用稍微不同的方法。

上面鏈接給出的例子: - 注意:他遇到了另一個他在這裏解決的問題(保持活力)。它在最初的示例代碼之後的鏈接中。

對象類來發送(記住[序列化]):

[serializable] 
public class Person { 
    private string fn; 
    private string ln; 
    private int age; 
    ... 
    public string FirstName { 
     get { 
     return fn; 
     } 
     set { 
     fn=value; 
     } 
    } 
    ... 
    ... 
    public Person (string firstname, string lastname, int age) { 
     this.fn=firstname; 
     ... 
    } 
} 

類發送對象:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters.Binary; 

class DataSender 
{ 
    public static void Main() 
    { 
    Person p=new Person("Tyler","Durden",30); // create my serializable object 
    string serverIp="192.168.0.1"; 

    TcpClient client = new TcpClient(serverIp, 9050); // have my connection established with a Tcp Server 

    IFormatter formatter = new BinaryFormatter(); // the formatter that will serialize my object on my stream 

    NetworkStream strm = client.GetStream(); // the stream 
    formatter.Serialize(strm, p); // the serialization process 

    strm.Close(); 
    client.Close(); 
    } 
} 

類爲接收對象:

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Runtime.Serialization; 
using System.Runtime.Serialization.Formatters.Binary; 

class DataRcvr 
{ 
    public static void Main() 
    { 
    TcpListener server = new TcpListener(9050); 
    server.Start(); 
    TcpClient client = server.AcceptTcpClient(); 
    NetworkStream strm = client.GetStream(); 
    IFormatter formatter = new BinaryFormatter(); 

    Person p = (Person)formatter.Deserialize(strm); // you have to cast the deserialized object 

    Console.WriteLine("Hi, I'm "+p.FirstName+" "+p.LastName+" and I'm "+p.age+" years old!"); 

    strm.Close(); 
    client.Close(); 
    server.Stop(); 
    } 
} 
1

TCP是基於流的協議(而不是數據報協議),所以它能夠接收通過閱讀方法調用sended數據的一部分。

要解決此問題,您可以使用DataLength字段(建議使用cornerback84),也可以使用您自己的「應用程序級數據包」結構。

例如,可以使用這樣的事情

|-------------------------------| 
|Begin|DataLength| Data |End| 
| 4b | 4b  | 1..MaxLen|4b | 
|-------------------------------| 

其中 開始 - 開始分組標識符(例如的0x0A,0x0B中,0x0C,0X0D) 數據長度 - 數據字段長度(例如,從0到MaxLength) 數據 - 實際數據(序列化Person類或其他數據) 端到端數據包標識符(例如0x01,0x05,0x07,0x0F)。

也就是說,在客戶端,您不僅要等待傳入數據,在接收到數據後您將搜索應用程序級數據包,並且只有在收到有效數據包後纔可以反序列化數據部分。