在C#中使用Tcpclient类发送和接收自定义对象

Rak*_*h K 9 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);
Run Code Online (Sandbox Code Playgroud)

反序列化的客户端代码是:

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;
Run Code Online (Sandbox Code Playgroud)

请帮我解决这个问题,并建议使用C#中的TcpClient在网络上传输自定义类对象的任何其他方法.

谢谢,拉克什.

ata*_*ata 11

在客户端接收时,您不知道要读取多少数据.您只依赖于ReceiveBufferSize,而您的数据可能会更大或更小.

我认为这里最好的方法是发送4个字节,告诉客户端传入数据的长度:

byte[] userDataLen = BitConverter.GetBytes((Int32)userDataBytes.Length);
netStream.Write(userDataLen, 0, 4);
netStream.Write(userDataBytes, 0, userDataBytes.Length);
Run Code Online (Sandbox Code Playgroud)

在接收端,您首先读取数据长度,然后读取确切的数据量.

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);
Run Code Online (Sandbox Code Playgroud)

事实上,我刚刚意识到,您可能需要做更多的事情来确保您阅读所有数据(只是一个想法,因为我没有尝试过,但只是因为你再次遇到问题,你可以试试这个).

NetworkStream.Read()方法返回一个数字,表示它已读取的数据量.输入数据可能比RecieveBuffer大.在这种情况下,您必须循环,直到您阅读所有数据.你必须做这样的事情:

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

    } while(dataRead < len);
}
Run Code Online (Sandbox Code Playgroud)


Kyl*_*ndo 9

看看这段代码.它需要稍微不同的方法.

上面链接给出的例子: - 注意:他面临的另一个问题是他在这里解决了(保持活着).它位于初始示例代码之后的链接中.

要发送的对象类(记住[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; 
      ... 
   } 
} 
Run Code Online (Sandbox Code Playgroud)

要发送对象的类:

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(); 
  } 
} 
Run Code Online (Sandbox Code Playgroud)

接收对象的类:

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(); 
  } 
}
Run Code Online (Sandbox Code Playgroud)