【问题标题】:Serializing object ready to send over TCPClient Stream序列化对象准备通过 TCPClient 流发送
【发布时间】:2011-12-12 12:58:05
【问题描述】:

我已经使用TcpListenerTcpClient 设置了服务器和客户端。

我想将一个对象发送到我的服务器应用程序进行处理。

我发现了 using System.Runtime.Serialization 和下面的 documentation,但我不想胡扯发现我在做这件事的方式很啰嗦。

问题:通过 TCP 流处理和发送对象的最佳方式是什么?

发送和接收。

这是我的对象的示例:

// Create a new house to send
house newHouse = new house();

// Set variables
newHouse.street = "Mill Lane";
newHouse.postcode = "LO1 BT5";
newHouse.house_number = 11;
newHouse.house_id = 1;
newHouse.house_town = "London";

【问题讨论】:

    标签: c# serialization tcp tcpclient tcplistener


    【解决方案1】:

    假设您有一个类House(在您的连接两端都可用)如下所示:

    [Serializable]
    public class House
    {
        public string Street { get; set; }
        public string ZipCode { get; set; }
        public int Number { get; set; }
        public int Id { get; set; }
        public string Town { get; set; }
    }
    

    您可以将类序列化为MemoryStream。然后,您可以像这样在TcpClient 连接中使用:

    // Create a new house to send house and set values.
    var newHouse = new House
        {
            Street = "Mill Lane", 
            ZipCode = "LO1 BT5", 
            Number = 11, 
            Id = 1, 
            Town = "London"
        };  
    
    var xmlSerializer = new XmlSerializer(typeof(House));
    var networkStream = tcpClient.GetStream();
    if (networkStream.CanWrite)
    {
        xmlSerializer.Serialize(networkStream, newHouse);
    }
    

    当然,您必须做更多的调查才能使程序无异常运行。 (例如,检查 memoryStream.Length 不大于 int,a.s.o.),但我希望我给了你正确的建议来帮助你;-)

    【讨论】:

    • 可以直接序列化到tcpClient的流中。
    • @L.B:感谢您的提示,我以这种方式更改了示例!
    【解决方案2】:

    首先创建一个空的ServerApplication和ClientApplication作为Console Application来简化示例。

    然后,将可序列化对象的定义放入单独的程序集中,然后将对共享程序集的引用添加到每个项目(服务器和客户端)。是否需要共享同一个对象,而不仅仅是相同的类副本。

    生成 DLL > 在解决方案资源管理器中的 Solution 'ServerApplication' 中右键单击 > 添加新项目... -> 选择类库 (例如,将此项目命名为 MySharedHouse) 将默认的 Class1 重命名为 House 并完成

    [Serializable]
    public class House
    {
        public string Street { get; set; }
        public string ZipCode { get; set; }
        public int Number { get; set; }
        public int Id { get; set; }
        public string Town { get; set; }
    }
    

    在 MySharedHouse 和 Build 中右键单击。

    现在 dll 已经构建好了,我们需要将它添加到服务器项目和客户端项目中。 在ServerApplication中右键单击>添加引用>浏览并找到dll,对于这个例子

    Projects\ServerApplication\MySharedHouse\bin\Debug\MySharedHouse.dll

    使用相同的 dll(相同的路径)在 ClientApplication 中重复该过程。

    现在您可以在 ServerApplication 和 ClientApplication 中将 House 类的实例用作单个对象,只需在顶部添加句子 “using MySharedHouse”

    服务器代码

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Threading;
    using MySharedHouse;
    
    namespace ServerApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                MessageServer s = new MessageServer(515);
                s.Start();
            }
        }
    
        public class MessageServer
        {
            private int _port;
            private TcpListener _tcpListener;
            private bool _running;
            private TcpClient connectedTcpClient;
            private BinaryFormatter _bFormatter;
            private Thread _connectionThread;
    
            public MessageServer(int port)
            {
                this._port = port;
                this._tcpListener = new TcpListener(IPAddress.Loopback, port);
                this._bFormatter = new BinaryFormatter();
            }
    
            public void Start()
            {
                if (!_running)
                {
                    this._tcpListener.Start();
                    Console.WriteLine("Waiting for a connection... ");
                    this._running = true;
                    this._connectionThread = new Thread
                        (new ThreadStart(ListenForClientConnections));
                    this._connectionThread.Start();
                }
            }
    
            public void Stop()
            {
                if (this._running)
                {
                    this._tcpListener.Stop();
                    this._running = false;
                }
            }
    
            private void ListenForClientConnections()
            {
                while (this._running)
                {
                    this.connectedTcpClient = this._tcpListener.AcceptTcpClient();
                    Console.WriteLine("Connected!");
                    House house = new House();
                    house.Street = "Evergreen Terrace";
                    house.ZipCode = "71474";
                    house.Number = 742;
                    house.Id = 34527;
                    house.Town = "Springfield";
                    _bFormatter.Serialize(this.connectedTcpClient.GetStream(), house);
                    Console.WriteLine("send House!");
                }
            }
        }
    }
    

    客户代码

    using System;
    using System.Net.Sockets;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.Threading;
    using MySharedHouse;
    
    namespace ClientApplication
    {
        class Program
        {
            static void Main(string[] args)
            {
                MessageClient client = new MessageClient(515);
                client.StartListening();
            }
        }
    
        public class MessageClient
        {
            private int _port;
            private TcpClient _tcpClient;
            private BinaryFormatter _bFormatter;
            private Thread _listenThread;
            private bool _running;
            private House house;
    
            public MessageClient(int port)
            {
                this._port = port;
                this._tcpClient = new TcpClient("127.0.0.1", port);
                this._bFormatter = new BinaryFormatter();
                this._running = false;
            }
    
            public void StartListening()
            {
                lock (this)
                {
                    if (!_running)
                    {
                        this._running = true;
                        this._listenThread = new Thread
                            (new ThreadStart(ListenForMessage));
                        this._listenThread.Start();
                    }
                    else
                    {
                        this._running = true;
                        this._listenThread = new Thread
                            (new ThreadStart(ListenForMessage));
                        this._listenThread.Start();
                    }
                }
            }
    
            private void ListenForMessage()
            {
                Console.WriteLine("Reading...");
                try
                {
                    while (this._running)
                    {
                        this.house = (House)this._bFormatter.Deserialize(this._tcpClient.GetStream());
                        Console.WriteLine(this.house.Street);
                        Console.WriteLine(this.house.ZipCode);
                        Console.WriteLine(this.house.Number);
                        Console.WriteLine(this.house.Id);
                        Console.WriteLine(this.house.Town);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    Console.ReadLine();
                }
            }
        }
    }
    

    哇!第一个通过 TCP/IP 发送的房子

    【讨论】:

      【解决方案3】:

      您可以简单地使用[Serializable] 属性装饰您的House 类。 (您不需要定义其他答案中发布的所有其他内容)

      然后您可以通过使用BinaryFormatter 类对其进行序列化来在线发送此对象。

      您是否考虑过设置 WCF 服务而不是使用 TcpListener 和 TcpClient ?让生活更轻松。

      例如,您可以定义一个返回房子的服务

      [ServiceContract]
      public interface IService
      {
          [OperationContract]
          House GetHouse(int houseId);
      }
      

      请参阅this 真实世界示例。

      【讨论】:

        【解决方案4】:

        您的回答暗示了以下对象(通常使用PascalCase 命名类):

        [Serializable]
        class House:ISerializable
        {
            public string Street {get; set;}
            public string PostalCode {get; set;}
            public int HouseNumber {get; set;}
            public int HouseID {get; set;}
            public string City {get; set;}
        
            public House() { }
            protected House(SerializationInfo info, StreamingContext context)
            {
                if (info == null)
                    throw new System.ArgumentNullException("info");
                Street = (string)info.GetValue("Street ", typeof(string));
                PostalCode = (string)info.GetValue("PostalCode", typeof(string));
                HouseNumber = (int)info.GetValue("HouseNumber", typeof(int));
                HouseID = (int)info.GetValue("HouseID", typeof(int));
                City = (string)info.GetValue("City", typeof(string));
            }
        
            [SecurityPermissionAttribute(SecurityAction.LinkDemand, 
                Flags=SecurityPermissionFlag.SerializationFormatter)]
            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
            {
                if (info == null)
                    throw new System.ArgumentNullException("info");
                info.AddValue("Street ", Street);
                info.AddValue("PostalCode", PostalCode);
                info.AddValue("HouseNumber", HouseNumber);
                info.AddValue("HouseID", HouseID );
                info.AddValue("City", City);
            }
        }
        

        现在你可以序列化你的对象了:

        void Send(Stream stream)
        {
            BinaryFormatter binaryFmt = new BinaryFormatter();
            House h = new House()
            {
                Street = "Mill Lane",
                PostalCode = "LO1 BT5",
                HouseNumber = 11,
                HouseID = 1,
                City = "London"
            };
        
            binaryFmt.Serialize(stream, h);
        }
        

        【讨论】:

          【解决方案5】:

          如何将 xml House 流反序列化回接收端的 House 对象? 我指的是 Fischermaen 的回答中给出的解决方案。

          在接收端,我可以使用以下命令在“输出”窗口中看到字符串表示:

          ASCIIEncoding encoder = new ASCIIEncoding();
                      System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
          

          提前谢谢你。

          编辑 *

          好的,这个解决方案对我有用。可能需要整理一下。

          这是一个反序列化字符串的方法:

          public static T DeserializeFromXml<T>(string xml)
              {
                  T result;
                  XmlSerializer ser = new XmlSerializer(typeof(T));
                  using (TextReader tr = new StringReader(xml))
                  {
                      result = (T)ser.Deserialize(tr);
                  }
                  return result;
              }
          

          然后从我的 TPC/IP 接收端调用该方法,如下所示:

          TcpClient tcpClient = (TcpClient)client;
                  NetworkStream clientStream = tcpClient.GetStream();
          
          
                  byte[] message = new byte[4096];
                  int bytesRead;
          
                  while (true)
                  {
                      bytesRead = 0;
          
                      try
                      {
                          //blocks until a client sends a message
                          bytesRead = clientStream.Read(message, 0, 4096);
                      }
                      catch
                      {
                          //a socket error has occured
                          break;
                      }
          
                      if (bytesRead == 0)
                      {
                          //the client has disconnected from the server
                          break;
                      }
          
          
                      //message has successfully been received
                      ASCIIEncoding encoder = new ASCIIEncoding();
                      System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
          
                      House house = DeserializeFromXml<House>(encoder.GetString(message, 0, bytesRead));
          
                      //Send Message Back
                      byte[] buffer = encoder.GetBytes("Hello Client - " + DateTime.Now.ToLongTimeString());
          
                      clientStream.Write(buffer, 0, buffer.Length);
                      clientStream.Flush();
                  }
          
                  tcpClient.Close();
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2013-06-17
            • 2014-02-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-07-16
            • 1970-01-01
            相关资源
            最近更新 更多