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

时间:2011-10-21 12:00:28

标签: c# serialization tcp tcpclient tcplistener

我使用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";

5 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:2)

您的回答意味着以下对象(通常使用驼峰案例命名类):

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

答案 2 :(得分:2)

您只需使用House属性装饰[Serializable]课程即可。 (您不需要定义其他答案中公布的所有其他内容)

然后,您可以使用BinaryFormatter类对其进行序列化,从而在线上发送此对象。

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

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

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

参见this现实世界的例子。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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

生成DLL &gt; 解决方案资源管理器中解决方案'ServerApplication'中的右键&gt;添加新项目... - &gt;选择类库 (例如,将此项目命名为 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; }
}

enter image description here

MySharedHouse和Build中的正确clic。

现在dll已构建,我们需要在Server Project和Client Project中添加它。 ServerApplication中的右clic&gt;添加参考&gt;浏览并找到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发送的房子