Сериализация объекта, готового к отправке через TCPClient Stream - PullRequest
5 голосов
/ 21 октября 2011

Сервер и клиент настроены с использованием 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";

Ответы [ 5 ]

9 голосов
/ 21 октября 2011

Предполагая, что у вас есть класс 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), но я надеюсь, что дал вам правильные предложения, которые помогут вам в вашем пути; -)

2 голосов
/ 21 октября 2011

Вы можете просто украсить свой класс House атрибутом [Serializable]. (Вам не нужно определять все остальные вещи, как указано в другом ответе)

Затем вы можете отправить этот объект на провод, сериализовав его, используя класс BinaryFormatter.

Рассматривали ли вы создание службы WCF вместо использования TcpListener и TcpClient? Делает жизнь намного проще.

Например, вы можете определить службу, которая вернула дом

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

См. этот пример из реального мира.

2 голосов
/ 21 октября 2011

Ваш ответ подразумевает следующий объект (это обычная практика называть классы, используя случай верблюда):

[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);
}
1 голос
/ 22 февраля 2018

Сначала создайте пустое 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; }
}

enter image description here

Щелкните правой кнопкой мыши в MySharedHouse and Build.

Теперь сборка dll собрана, и нам нужно добавить ее в Project Server и Client Project.Щелкните правой кнопкой мыши ServerApplication> Add Reference> Просмотрите и найдите dll, для этого примера

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

Повторитепроцесс в ClientApplication с использованием того же самого dll (тот же путь).

Теперь вы можете использовать экземпляры класса House в ServerApplication и ClientApplication в качестве единого объекта, просто добавив предложение "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();
            }
        }
    }
}

Wooala!первый дом для отправки по TCP / IP

1 голос
/ 13 ноября 2012

Как бы вы десериализовали поток xml House обратно в объект House на принимающей стороне?Я ссылаюсь на решение, приведенное в ответе Фишермэна.

На моем получающем конце я вижу строковое представление в моем окне вывода, используя следующее:

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();
    }
...