Sending and receiving custom objects using Tcpclient class in C#

后端 未结 3 739
孤独总比滥情好
孤独总比滥情好 2021-02-01 09:52

I have a client server application in which the server and the client need to send and receive objects of a custom class over the network. I am using TcpClient class for transmi

相关标签:
3条回答
  • 2021-02-01 10:16

    TCP is stream-based protocol (as opposed to datagram protocol) so it's possible to receive only part of sended data via Read method call.

    To solve this problem you may use DataLength field (as cornerback84 suggested) or you may use your own "application-level packet" structure.

    For example, you may use something like this

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

    where Begin - start packet identifier (for example 0x0A, 0x0B, 0x0C, 0x0D) DataLength - Data field length (for example, from 0 to MaxLength) Data - actual data (serialized Person class or some other data) End - end packet identifier (for example 0x01, 0x05, 0x07, 0x0F).

    That is, on client side you would wait not only for incoming data, after receiving data you would search you Application level packets, and you may deserialized Data part only after receiving valid packet.

    0 讨论(0)
  • 2021-02-01 10:20

    When receiving on client side you do not know how much data you want to read. You are only relying on the ReceiveBufferSize, while your data can be larger or smaller then that.

    I think the best approach here is to send 4 bytes that tells your client about the length of incoming data:

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

    and on the recieving end you first read the data length and then read exact amount of data.

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

    Infact, I just realized, that you might has to do a little more to assure you read all data (just an idea because I haven't tried it, but just incase you run into problem again you can try this).

    The NetworkStream.Read() method returns a number indicating the amount of data it has read. It might be possible that the incoming data is larger then the RecieveBuffer. In that case you have to loop until you read all of the data. You have to do something like this:

    SafeRead(byte[] userData, int len)
    {
        int dataRead = 0;
        do
        {       
            dataRead += readNetStream.Read(readMsgData, dataRead, len - dataRead);
    
        } while(dataRead < len);
    }
    
    0 讨论(0)
  • 2021-02-01 10:27

    Have a look at this code. It takes a slightly different approach.

    Example given by the link above: - Note: there was another problem he was facing which he solved here (keep-alive). It's in the link after the initial sample code.

    Object class to send (remember the [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; 
          ... 
       } 
    } 
    

    Class to send object:

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

    Class to receive object:

    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(); 
      } 
    }
    
    0 讨论(0)
提交回复
热议问题