написание прослушивателя TCP с объектом в качестве параметра

#c# #tcp #streamreader #streamwriter

#c# #tcp #streamreader #streamwriter

Вопрос:

Я понимаю, как отправить объект через TCP-соединение на C #; Я использую StreamWriter .

Как я могу получить этот объект с другой стороны?

Я пробовал StreamReader , но он не содержит параметра типа object .

Как это можно сделать?

Комментарии:

1.Вам следует прочитать некоторые документы о Serialization , XmlSerializer BinaryFormatter SoapFormatter DataContractSerializer JavaScriptSerializer и т. Д

2. StreamReader / Writer предназначены для текста. Если вы не пишете текст, зачем вы пишете текст?

3. Ну, вот что я хочу сделать: я хочу отправить массив изображений: Image[] ImgArr = new ImgArr [2]; object Obj = (object) ImgArr; Я хочу отправить ‘Obj’ и получить его на стороне клиента в качестве другого объекта для использования внутренних изображений…

4. Я сериализовал массив объектов, содержащий 2 массива байтов для двух изображений, это сработало, и я успешно получил оба изображения, но поток слишком медленный…

Ответ №1:

Лучшее решение IMO — использовать BinaryReader для чтения из потока. Кроме того, вы должны написать поток, используя BinaryWriter class .

Если ваш объект не относится к одному из базовых типов, вам необходимо выполнить сериализацию перед отправкой его через TCP-соединение.

Комментарии:

1. Ну, вот что я хочу сделать: я хочу отправить массив изображений: Image[] ImgArr = new ImgArr [2]; object Obj = (object) ImgArr; Я хочу отправить ‘Obj’ и получить его на стороне клиента в качестве другого объекта для использования внутренних изображений…

2. Вы должны сериализовать свой объект с помощью BinaryFormatter и десериализовать его с другой стороны: msdn.microsoft.com/en-us/library /…

Ответ №2:

Я действительно могу порекомендовать использовать ProtoBuf для преобразования ваших объектов в / из байтов, которые вы можете отправлять по сети (т.Е. сериализации). Это намного лучше, чем встроенные двоичные сериализаторы (скорость, управление версиями).

Он также имеет дружественные помощники для добавления префикса длины / размера к записанным данным, что полезно, когда вам нужно прочитать данные на принимающей стороне.

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

 public class Messenger
{
    private readonly TcpClient client;

    private readonly NetworkStream stream;

    public IPEndPoint RemoteEndPoint { get { return (IPEndPoint) client.Client.RemoteEndPoint; } }

    public Messenger( TcpClient client )
    {
        this.client = client;
        stream = client.GetStream();
    }

    #region Send and Receive
    public TResponse SendReceive<TRequest, TResponse>( TRequest request ) where TRequest : Message where TResponse : Message
    {
        Send( request );
        return Receive<TResponse>();
    }

    public void Send<TRequest>( TRequest request ) where TRequest : Message
    {
        using( var ms = new MemoryStream())
        {
            Serializer.SerializeWithLengthPrefix( ms, request, PrefixStyle.Fixed32 );
            stream.Write( ms.GetBuffer(), 0, (int) ms.Length );
            stream.Flush();
        }
    }

    public TResponse Receive<TResponse>() where TResponse : Message
    {
        try
        {
            return GetMessage<TResponse>();
        }
        catch (Exception ex)
        {
            if (ex is IOException || ex is InvalidOperationException)
            {
                stream.Dispose();
            }
            throw;
        }
    }
    #endregion

    #region Helpers
    private TMessage GetMessage<TMessage>() where TMessage : Message
    {
        int messageLength = BitConverter.ToInt32(GetBytes(stream, 4), 0);
        byte[] data = GetBytes(stream, messageLength);
        using (var ms = new MemoryStream(data))
        {
            return Serializer.Deserialize<TMessage>(ms);
        }
    }

    private static byte[] GetBytes(NetworkStream stream, int length)
    {
        int bytesRequired = length;
        int bytesRead = 0;
        var bytes = new byte[length];
        do
        {
            while( !stream.DataAvailable )
                Thread.Sleep( 100 );                    
            int read = stream.Read(bytes, bytesRead, bytesRequired);
            bytesRequired -= read;
            bytesRead  = read;
        }
        while (bytesRequired > 0);
        return bytes;
    }
    #endregion
}
  

Примечание: класс Serializer взят из библиотеки ProtoBuf.

Комментарии:

1. Ну, вот что я хочу сделать: я хочу отправить массив изображений: Image[] ImgArr = new ImgArr [2]; object Obj = (object) ImgArr; Я хочу отправить ‘Obj’ и получить его на стороне клиента в качестве другого объекта для использования внутренних изображений…

2. Затем создайте класс со свойством Images (типа Image[] ), украсьте его необходимыми атрибутами ProtoBuf (изучите документы для получения справки по этому вопросу или выполните поиск в StackOverflow) и используйте метод Send для его передачи (и, очевидно, получения на принимающей стороне).).

Ответ №3:

Если вы не хотите или не можете использовать BinaryFormatter, вам придется десериализовать объект yoursekf

Модифицированный пример из msdn для двоичного форматирования:

Допустим, у вас есть модель:

 [Serializable] //you could also make the class implement ISerializable
class SomeModel
{
    public String Name
    {
        get;
        set;
    }
}
  

у вас есть свой сетевой поток:

 NetworkStream ns;
  

базовый пример того, как вы выполняете часть сериализации:

 void Serialize() 
{
    SomeModel myModel = new SomeModel()
    {
        Name = "mooo"
    };

    // Construct a BinaryFormatter and use it to serialize the data to the stream.
    BinaryFormatter formatter = new BinaryFormatter();
    try 
    {
        formatter.Serialize(ns, myModel);
    }
    catch (SerializationException e) 
    {
        throw e;
    }
}
  

а что касается десериализации

 void Deserialize() 
{
    SomeModel myModel;
    try 
    {
        BinaryFormatter formatter = new BinaryFormatter();

        // Deserialize the object from the stream and 
        // assign the reference to the local variable.
        myModel = (SomeModel) formatter.Deserialize(ns);
    }
    catch (SerializationException e) 
    {
        throw e;
    }
}
  

Есть одна вещь, о которой вы должны позаботиться.
Вы должны знать, что при десериализации вы не можете просто вставить в него поток и получить что-то обратно, вы должны знать, что то, что вы собираетесь прочитать, на самом деле является сериализованным SomeModel-классом.
Я не знаю, как выглядит ваш код, поэтому я не знаю, является ли это проблемой или нет, и если да, то как ее избежать.

В любом случае, вашим вторым выбором было бы использовать BinaryReader. Вам нужно будет создать экземпляр вашего объекта и прочитать данные вручную.

 using(BinaryWriter writer =
    new BinaryWriter(ns))
{
    binWriter.Write(myModel.Name);
}
  

чтение:

  using(BinaryReader reader =
        new BinaryReader(ns))
 {
    try
    {
        SomeModel myModel = new SomeModel();

        myModel.Name = binReader.ReadString();
    }

    // If the end of the stream is reached before reading
    // the four data values, ignore the error and use the
    // default settings for the remaining values.
    catch(EndOfStreamException e)
    {
        Console.WriteLine("{0} caught and ignored. "  
            "Using default values.", e.GetType().Name);
    }
}
  

Комментарии:

1. Ну, вот что я хочу сделать: я хочу отправить массив изображений: Image[] ImgArr = new ImgArr [2]; object Obj = (object) ImgArr; Я хочу отправить ‘Obj’ и получить его на стороне клиента в качестве другого объекта для использования внутренних изображений…