2010-02-23 9 views
9

Ho un'applicazione client server in cui il server e il client devono inviare e ricevere oggetti di una classe personalizzata sulla rete. Sto usando la classe TcpClient per la trasmissione dei dati. Sto serializzando l'oggetto dal lato del mittente e inviando il flusso di byte risultante al ricevitore. Ma al ricevitore, quando provo a de-serializzare i byte ricevuti, si getta serializzazione delle eccezioni ei dettagli sono:Invio e ricezione di oggetti personalizzati utilizzando la classe Tcpclient in C#

flusso L'ingresso non è un formato binario valido. I contenuti iniziali (in byte) sono: 0D-0A-00-01-00-00-00-FF-FF-FF-01-00-00-00-00 ...

il mio codice server che serializza l'oggetto è:

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

il codice client che de-serializza è:

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; 

per favore mi aiuti a risolvere questo problema ed eventualmente suggerire qualsiasi altro metodo per trasmettere oggetti di classi personalizzate acr oss network che utilizza TcpClient in C#.

Grazie, Rakesh.

risposta

11

Quando si riceve su lato client non sai quanti dati vuoi leggere. Si sta solo facendo affidamento su ReceiveBufferSize, mentre i dati possono essere maggiori di o inferiori.

penso che l'approccio migliore è quello di trasmettere 4 byte che indica al cliente circa la lunghezza dei dati in entrata:

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

e alla fine recieving di leggere prima la lunghezza dei dati e quindi leggere importo esatto di dati.

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); 

Infatti, ho appena realizzato, che si potrebbe ha a che fare un po 'di più per assicurare di leggere tutti i dati (solo un'idea, perché non l'ho provato, ma solo in caso si esegue in problemi ancora una volta si può prova questo).

Il metodo NetworkStream.Read() restituisce un numero che indica la quantità di dati letti. Potrebbe essere possibile che i dati in arrivo siano più grandi di RecieveBuffer.In tal caso, è necessario eseguire il ciclo finché non si leggono tutti i dati. Devi fare qualcosa di simile:

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

    } while(dataRead < len); 
} 
7

Dai un'occhiata allo this code. Ci vuole un approccio leggermente diverso.

Esempio fornito dal collegamento sopra: - Nota: c'era un altro problema che stava affrontando e che ha risolto qui (keep-alive). È nel collegamento dopo il codice di esempio iniziale.

classe Object per inviare (ricordate la [Serializable]):

[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; 
     ... 
    } 
} 

Classe di inviare oggetto:

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(); 
    } 
} 

classe per ricevere oggetto:

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 è un protocollo basato sul flusso (al contrario di datagram protocol), quindi è possibile ricevere solo una parte dei dati Inviati via Leggi chiamata di metodo.

Per risolvere questo problema è possibile utilizzare il campo DataLength (come suggerito da cornerback84) oppure è possibile utilizzare la propria struttura "pacchetto a livello di applicazione".

Ad esempio, si può usare qualcosa di simile

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

dove Begin - avviare identificatore di pacchetti (per esempio 0x0A, 0x0B, 0x0C, 0x0D) dataLength - lunghezza del campo di dati (ad esempio, da 0 a MaxLength) Dati - dati effettivi (classe Persona serializzata o altri dati) Identificatore di pacchetto end-end (ad esempio 0x01, 0x05, 0x07, 0x0F).

Cioè, sul lato client si aspetterebbe non solo per i dati in arrivo, dopo aver ricevuto i dati si sarebbero cercati i pacchetti a livello di applicazione e si potrebbe deserializzare la parte dati solo dopo aver ricevuto un pacchetto valido.

Problemi correlati