#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. Спасибо за ваш вклад. На самом деле это по пути, который я рассматривал (возможно, на самом деле немного аккуратнее).