Шаблон для общей коммуникационной среды (обработка и предоставление полученных данных)

#c# #xml #sockets #generics #types

#c# #xml #сокеты #универсальные #типы

Вопрос:

В настоящее время я проделал значительную часть пути разработки реализации общего коммуникационного «фреймворка» на основе сокетов, который работает между C # и Android.

Создать сервер и клиент было достаточно просто. В этом нет ничего слишком сложного с технической точки зрения.

На данный момент реализация содержит 2 основных компонента.

AndroidTcpListener (класс tcp-сокета, который обрабатывает обмен данными)
AndroidTcpServer (класс, который создает экземпляр TcpListener и обрабатывает данные, полученные (от TcpListener) при получении данных)

Данные передаются между клиентом и сервером с использованием стандартного XML-сериализатора (в какой-то момент я могу перейти на JSON, но на данный момент я остановился на XML).

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

Я пробовал использовать дженерики, но независимо от того, как я их структурирую, кажется, что в конце концов мне приходится создавать AndroidTcpServer<T> , который затем ограничивает меня одним типом данных.

Я попытался создать класс CustomActivator как таковой

 class CustomActivator<T> where T : Serialization.ITcpSerializable<T>
{
    public CustomActivator(String xmlTypeName, Byte[] data) 
    {
        this.XmlTypeName = xmlTypeName;
        this.Data = data;
    }

    public String XmlTypeName { get; set; }
    public Byte[] Data { get; set; }

    public T Activate()
    {
        T res =  Activator.CreateInstance<T>();
        using (MemoryStream stream = new MemoryStream(this.Data))
        {
            return res.Deserialize(XElement.Load(stream));               
        }
    }
}
  

Но опять же, это, похоже, ограничивает меня в реализации AndroidTcpServer<T>

Я попытался создать класс AndroidTcpEvent, который структурирован как таковой

 public class AndroidTcpEvent<T>
{
    public AndroidTcpEvent() { }
    public AndroidTcpEvent(String eventName, T data)
    {
        this.EventName = eventName;
        this.Data = data;
    }

    public String EventName { get; set; }
    public T Data { get; set; }
}
  

Но XmlSerializer, похоже, отказывается сериализовать его (я думаю, я мог бы реализовать свой собственный сериализатор, но это далеко не тривиально и на самом деле не решает, как я буду предоставлять данные)

Я пробовал бесчисленное множество других подходов, которые, похоже, где-то зашли в тупик.

Итак, в основном на данный момент у меня есть (внутри класса AndroidTcpServer метод как таковой

 private void DataReceived(Object sender, TcpServerMessageReceivedEventArgs e)
{
    //e.Data = Byte[] of XML serialized data of the message sent (Semantics are unimportant)
}
  

В заключение я надеюсь на некоторые рекомендации о том, как обычно следует обрабатывать предоставление полученных данных и фактически создавать экземпляры данных в такой универсальной среде, и есть ли какие-либо известные шаблоны, которые можно использовать для решения этой проблемы?

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

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

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

1. Хорошо, откуда вы знаете, какие объекты пересекаются по проводам? Все сериализаторы xml, которые я видел, требуют, чтобы вы знали тип, который вы хотите десериализовать.

2. Вы знаете, потому что это выражено как часть XML, и вы можете использовать отражение для сопоставления типов (Т. Е.: <ArrayOfStock>). Не идеально, но это работает. В качестве альтернативы я рассматриваю возможность передачи другой группы XML с каждой передачей, которая содержит информацию о типе. Пока не уверен…

Ответ №1:

Во-первых, я думаю, что ответственность сервера должна заканчиваться вызовом TcpServerMessageReceived события. Его задача — прослушивать входящие соединения и получать данные, а затем уведомлять всех, кто заинтересован.

Затем вы можете прикрепить свой пользовательский активатор к этому событию, возможно, следующим образом:

 class CustomActivator<T> where T : Serialization.ITcpSerializable<T>
{
    public void ListenTo(AndroidTcpServer server)
    {
         server.TcpServerMessageReceived  = DataReceived;
    }

    private void DataReceived(object sender, TcpServerMessageReceivedEventArgs e)
    {
        T res =  Activator.CreateInstance<T>();
        using (MemoryStream stream = new MemoryStream(e.Data))
        {
            OnObjectReceived(res.Deserialize(XElement.Load(stream));               
        }
    }

    protected void OnObjectReceived(T obj)
    {
        var handler = ObjectReceived;
        if (handler != null)
        {
            OnObjectReceived(this, new ObjectReceivedEventArgs(obj));
        }
    }
}
  

Я не уверен в типах объектов, которые вы отправляете по проводам. Если вы можете определить тип, который содержится в сериализованном xml, прежде чем вам действительно понадобится десериализовать весь объект, тогда вы можете изменить свой CustomActivator , чтобы он проверял тип и игнорировал его, если он не является его собственным T (надеюсь, это имеет смысл), и вы прикрепляете активаторы для каждого типа, который вы можете получить (возможно, вы можете использовать отражение для автоматического прикрепления всех типов, реализующих ISerializable интерфейс).

Обновление: Если вы можете угадать тип из входящих данных, вы могли бы сделать что-то вроде этого:

 class GenericActivator
{
    public void ListenTo(AndroidTcpServer server)
    {
         server.TcpServerMessageReceived  = DataReceived;
    }

    private void DataReceived(object sender, TcpServerMessageReceivedEventArgs e)
    {
        var t = Type.GetType(GuessTypeName(e.Data));

        var res =  Activator.CreateInstance(t) as typeof(ITcpSerializable<>).MakeGenericType(t);
        using (MemoryStream stream = new MemoryStream(e.Data))
        {
            OnObjectReceived(res.Deserialize(XElement.Load(stream));               
        }
    }

    protected void OnObjectReceived(object obj)
    {
        var handler = ObjectReceived;
        if (handler != null)
        {
            OnObjectReceived(this, new ObjectReceivedEventArgs(obj));
        }
    }
}
  

Вы также могли бы добавить реестр типов в GenericActivator , где вы можете регистрировать прослушивателей для каждого типа следующим образом:

 class GenericActivator
{
     private Dictionary<Type, List<Action<object>> _TypeListeners;

     public void Register<T>(Action<object> objectReceived)
     {
         List<Action<object>> listeners;
         if (!_TypeListeners.TryGet(typeof(T), out listeners)
         {
             listeners = new List<Action<object>>();
         }
         listeners.Add(objectReceived);
     }
}
  

И затем вызывайте прослушиватели только для типа received. Делает предположение, что GuessTypeName работает надежно.

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

1. Спасибо за ваш вклад. На самом деле это по пути, который я рассматривал (возможно, на самом деле немного аккуратнее).