序列化对象准备通过 TCPClient Stream 发送

发布于 2024-12-11 08:05:32 字数 641 浏览 3 评论 0原文

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

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

我发现了 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";

I've got a server and client set up using TcpListener and TcpClient.

I want to send an object to my server application for processing.

I've discovered the using System.Runtime.Serialization and the following documentation, but I didn't want to faff around to find that I'm doing it in long winded way.

The question: What is the best way to process and send an object over the TCP stream?

Sending and receiving.

Here's an example of my object:

// 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";

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

惯饮孤独 2024-12-18 08:05:32

假设您有一个类 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),但我希望我给你正确的建议来帮助你;-)

Assuming you have a class House (available on both sides of your connection) looking like this:

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

You can serialize the class into a MemoryStream. You can then use in your TcpClient connection like this:

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

Of course you have to do a little more investigation to make the program running without exception. (e.g. Check memoryStream.Length not to be greater than an int, a.s.o.), but I hope I gave you the right suggestions to help you on your way ;-)

风吹过旳痕迹 2024-12-18 08:05:32

首先创建一个空的 ServerApplication 和 ClientApplication 作为控制台应用程序以简化示例。

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

生成 DLL >
在解决方案资源管理器中右键单击解决方案“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 中右键单击并构建。

现在 dll 已构建完毕,我们需要将其添加到服务器项目和客户端项目中。
在 ServerApplication 中右键单击 >添加参考>浏览并找到 dll,对于本例

项目\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 发送的房子

First create a empty ServerApplication and ClientApplication as Console Application to simplify the example.

Then, put the definition for the serializable object into a separate assembly and then add a reference to the shared assembly to each project (server and client). Is necesary share the same object, not just an identical class copy.

To Generate DLL >
Right clic in Solution 'ServerApplication' in the Solution Explorer > Add New Project... -> select Class Library
(e.g. name this project MySharedHouse)
Rename the default Class1 to House and complete it

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

enter image description here

Right clic in MySharedHouse and Build.

Now the dll is build and we need to add it in Server Project and Client Project.
Right clic in ServerApplication > Add Reference > Browse and find the dll, for this example

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

Repeat the process in ClientApplication using the same dll (same path).

Now you can use instances of House class in ServerApplication and ClientApplication as a single object, simply adding the sentence "using MySharedHouse" at the top.

SERVER CODE

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!");
            }
        }
    }
}

CLIENT CODE

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! the first house to be sent over TCP/IP

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

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

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

例如,您可以定义返回房屋的服务

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

请参阅这个现实世界的例子。

You can simply decorate your House class with the [Serializable] attribute. (You do not need to define all the other stuff as posted in the other answer)

You can then send this object on the wire by serializing it using the BinaryFormatter class.

Have you considered setting up a WCF service instead of using TcpListener and TcpClient ? Makes life a lot easier.

For instance you could define a service that returned a house

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

See this real world example.

客…行舟 2024-12-18 08:05:32

您的答案暗示了以下对象(通常的做法是使用 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);
}

Your answer implies the following object (it is common practice to name classes using 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);
    }
}

Now you can serialize your objects:

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);
}
清引 2024-12-18 08:05:32

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

How would you deserialize the xml House stream back to a House object on the receiving end?
I'm refering to the solution given in Fischermaen's answer.

On my recieving end I can see a string representation in my Output window by using the following:

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

Thank you in advance.

EDIT *

Ok well this solution has worked for me. Might need some tidying up.

Here's a method to deserialize a string:

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

Then from my TPC/IP Recieving end I call the method like so:

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();
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文