序列化对象准备通过TCPClient Stream发送
我已经使用TcpListener
和TcpClient
设置了服务器和客户端。
我想将一个对象发送到我的服务器应用程序进行处理。
我已经发现了using System.Runtime.Serialization
和以下文档 ,但我不想发现我正在以漫长的方式做这件事。
问题:通过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";
假设您有一个类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,aso),但我希望我给你正确的建议,以帮助你的方式;-)
您的答案意味着以下对象(通常的做法是使用camel case命名类):
[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); }
您可以使用[Serializable]
属性简单地装饰您的House
类。 (您不需要定义其他答案中公布的所有其他内容)
然后,您可以通过使用BinaryFormatter
类对其进行序列化来在线上发送此对象。
您是否考虑过设置WCF服务而不是使用TcpListener和TcpClient? 让生活更轻松。
例如,您可以定义返回房屋的服务
[ServiceContract] public interface IService { [OperationContract] House GetHouse(int houseId); }
看到这个真实世界的例子。
您如何将xml House流反序列化回接收端的House对象? 我正在参考Fischermaen的回答中给出的解决方案。
在我的接收结束时,我可以使用以下内容在“输出”窗口中看到字符串表示:
ASCIIEncoding encoder = new ASCIIEncoding(); System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
先感谢您。
编辑*
好的,这个解决方案对我有用。 可能需要一些整理。
这是一个反序列化字符串的方法:
public static T DeserializeFromXml(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(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(); }
首先创建一个空的ServerApplication和ClientApplication作为控制台应用程序来简化示例。
然后,将可序列化对象的定义放入单独的程序集中,然后将共享程序集的引用添加到每个项目(服务器和客户端)。 必要的是共享相同的对象,而不仅仅是相同的类副本。
在解决方案资源管理器>添加新项目中解决方案’ServerApplication’中生成DLL >右clic … – >选择类库(例如,将此项目命名为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中正确的clic。
现在构建了dll,我们需要在Server Project和Client Project中添加它。 对于此示例,在ServerApplication> Add Reference> Browse中找到正确的clic并找到dll
项目\ ServerApplication \ MySharedHouse \ BIN \调试\ MySharedHouse.dll
使用相同的dll(相同路径)在ClientApplication中重复此过程。
现在,您可以将ServerApplication和ClientApplication中的House类实例用作单个对象,只需在顶部添加“使用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(); } } } }
Wooala! 第一个通过TCP / IP发送的房子