У меня возникли проблемы с созданием сетевого интерфейса для очень простой игры, которую я сделал в Xna. Мне просто нужно было отправлять объекты через TCP-клиент/Socket. Пример: У меня есть класс под названием «Игрок». В каждом игроке есть имя поля «Информация», типа «PlayerInfo». В клиенте/сервере мне нужно будет отправлять информацию каждому игроку каждому клиенту, кроме того, кто его отправил (очевидно). Это простой пример, но мне нужно будет сделать это примерно с 5 - 10 объектами, а также отправить обновления (позиции, действия ...) Есть ли простой способ сделать это с помощью TCP/Sock? Примечание: Я бы оценил свои знания в C# и программировании как 6/10, поэтому вам не нужно все объяснять, если у вас есть решение (пример: какая разница между переменной и полем). Я также знаю об интерфейсах, библиотеках и т. Д. Заранее благодарю!Отправлять типизированные объекты через tcp или сокеты
ответ
У меня есть один подход, который я бы рекомендовал, и два меньших, которые зависят от многих вещей.
Первый подразумевает, что вы уже знаете, как использовать класс Socket, но у вас много классов, которые вам нужно отправить через него.
С транспортной точки зрения вы должны создать/принять во внимание только один очень простой класс. Назовем этот класс MyMessage:
public class MyMessage {
public byte[] Data { get; set; }
}
Ok. С точки зрения TCP все, что вам нужно сделать, это убедиться, что вы можете передавать экземпляры этого класса (от клиентов до сервера и обратно). Я не буду углубляться в подробности этого, но я укажу, что если вам удастся это сделать, вы измените характер TCP/IP-соединения из «байтового потока» в «поток сообщений». Это означает, что, как правило, TCP/IP не гарантирует, что куски данных, которые вы отправляете через соединение, прибывают в пункт назначения в тех же формациях (их можно объединить или разделить).Единственное, что он гарантирует, это то, что байты всех кусков в конечном итоге поступят в том же порядке на другом конце соединения (всегда).
Теперь, когда у вас есть поток сообщений и вы можете использовать хорошую старую сериализацию .NET для инкапсуляции любого экземпляра класса внутри свойства Data. Это то, что он сериализует графы объектов в байты и наоборот.
, как вы это сделать (чаще всего), чтобы использовать стандартный класс библиотеки: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter , который можно найти в mscorlib.dll как так:
public static class Foo {
public static Message Serialize(object anySerializableObject) {
using (var memoryStream = new MemoryStream()) {
(new BinaryFormatter()).Serialize(memoryStream, anySerializableObject);
return new Message { Data = memoryStream.ToArray() };
}
}
public static object Deserialize(Message message) {
using (var memoryStream = new MemoryStream(message.Data))
return (new BinaryFormatter()).Deserialize(memoryStream);
}
}
Класс BinaryFormatter способен перемещаться по дереву/графу объектов, начиная с корня/стража, предоставленного в качестве второго аргумента метода Serialize (Stream, object), и записывать все примитивные значения плюс информацию о типе и относительную информацию о местоположении предоставленный поток. Он также может выполнять точный обратный и десериализовать весь графический объект, если предоставленный поток помещается в соответствии с местом бывшей сериализации графа объекта.
Здесь есть несколько уловов: вам нужно будет аннотировать все ваши классы с помощью [SerializableAttribute]. Если ваши классы содержат поля, которые имеют другие классы, написанные вами, и вы сказали, что они делают:
[SerializableAttribute]
public class Player {
public PlayerInfo Info;
//... etc
, то вам нужно аннотировать те с [SerializableAttribute] тоже:
[SerializableAttribute]
public class PlayerInfo { //... etc
Если ваши классы содержат поля, относящиеся к типам, написанным другими (например, Microsoft), тогда их лучше было бы аннотировать с атрибутом. Большинство из тех, которые могут быть сериализованы, уже есть. Примитивные типы естественно сериализуемы. Вещи, которые не должно быть Сериализованные: потоковые видео, нитки, розетка и т.д.
После того, что у вас есть сериализуемые классы все, что вам нужно сделать, это сериализовать их экземпляры, отправлять их, получать их и десериализацию их:
class Client {
public static void SendMovement(Movement movement) {
Message message = Foo.Serialize(movement);
socketHelper.SendMessage(message);
}
public static void SendPlayer(Player player) {
Message message = Foo.Serialize(player);
socketHelper.SendMessage(message);
}
// .. etc
public static void OnMessageReceivedFromServer(Message message) {
object obj = Foo.Deserialize(message);
if (obj is Movement)
Client.ProcessOtherPlayersMovement(obj as Movement);
else if (obj is Player)
Client.ProcessOtherPlayersStatusUpdates(obj as Player);
// .. etc
}
public static void ProcessOtherPlayersMovement(Movement movement) {
//...
}
// .. etc
}
в то время как на стороне сервера:
class Server {
public static void OnMessageReceived(Message message, SocketHelper from, SocketHelper[] all) {
object obj = Foo.Deserialize(message);
if (obj is Movement)
Server.ProcessMovement(obj as Movement);
else if (obj is Player)
Server.ProcessPlayer(obj as Player);
// .. etc
foreach (var socketHelper in all)
if (socketHelper != from)
socketHelper.SendMessage(message);
}
}
Вам нужен проект общей сборки (библиотеки классов), чтобы ссылаться на оба исполняемых проектах (клиент и сервер).
Все ваши классы, которые необходимо пройти, должны быть записаны на этой сборке, чтобы как сервер, так и клиент знали, как понимать друг друга на этом очень подробном уровне.
Если серверу не нужно понимать, что говорится между клиентами и передавать только сообщения (передавая одно сообщение другим клиентам N-1), забудьте о том, что я сказал об общей сборке. В этом конкретном случае сервер видит только байты, в то время как клиенты имеют более глубокое понимание фактических сообщений, отправляемых туда и обратно.
Я сказал, что у меня есть три подхода.
Вторая связана с .NET Remoting, которая может занять много работы с ваших плеч, но с которой трудно жить, если вы не полностью ее понимаете.Вы можете прочитать об этом на MSDN, здесь: http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.100).aspx
Третья была бы лучше, только если (сейчас или в будущем) XNA вы имеете в виду Windows Phone или другую реализацию XNA, которая не поддерживает класс BinaryFormatter (ExEn с MonoTouch или другими). В таком случае вам будет сложно, если вам понадобится ваш сервер (полномасштабное, старомодное приложение .NET), чтобы ссылаться на общую сборку, о которой я говорил, а также иметь игровой проект (который не был бы хорошим старомодным .NET, но имеют довольно экзотический характер) ссылаются на ту же самую сборку.
В этом случае нам нужно будет использовать и чередовать форму сериализации и десериализации ваших объектов. Вам также необходимо будет одинаково реализовать два набора классов в двух мирах (.NET и WP7 или WP8). Вы можете использовать некоторую форму XML-сериализаторов, которые вам нужно будет явно сопоставлять с вашими классами (не такими мощными, как класс BinaryFormatter, но более универсальными в том, что характер среды выполнения, в которой размещаются ваши классы).
Вы можете прочитать о классе XmlSerializer на MSDN, здесь: http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx
Все эти решения очень интересны, но единственная проблема, о которой я могу думать, - это выяснить, какой тип на стороне приемника? Если я десериализую что-нибудь, смогу ли я получить тип для трансляции? Спасибо за все ответы! Очень полезно. –
Если вы используете подход BinaryFormatter, каким-то образом это будет так, как если бы два коллеги были в одном процессе. Короче: у вас есть классовая проблема, когда вы бросаете кучу классов в объект, а затем пытаетесь выяснить, что на самом деле есть объекты (Player, Movement, Interaction?). Поэтому, если вы используете что-то вроде if (obj is Player) {Player asPlayer = obj as Player;/* сделайте определенную обработку игрока */вы решите свой вопрос. Метод десериализации уже знает (во время выполнения), что вам нужно только для отправки (это игрок или нет, это ход и т. Д.). –
Это просто логически говоря, Microsoft не могла догадаться, что вы в 2013 году напишите класс игрока и нуждаетесь в его десериализации. Таким образом, при синтаксическом, письменном, временном уровне, этот метод возвращает объект. –
Вы можете создать собственное решение, используя различные классы, предусмотренные в .net framework. Вы хотите проверить WCF или Sockets namepsace, в частности классы TcpClient и TcpListener, см. MSDN. Если вы выполняете поиск, связанный с их использованием, загружайте большие учебники. Вам также нужно будет рассмотреть, как превратить ваши типизированные объекты в массивы байтов, похожие на это question.
Альтернативный подход заключается в использовании сетевой библиотеки. Существуют библиотеки низкого уровня и библиотеки высокого уровня. Учитывая ваш уровень программирования и конкретную конечную цель, я бы предложил библиотеку высокого уровня. Примером такой сетевой библиотеки будет lidgren. Я разработчик другой сетевой библиотеки networkComms.net и быстрый пример того, как вы можете отправить типизированных объектов с помощью этой библиотеки следующим образом:
Shared Base (определяет объект Player):
[ProtoContract]
class Player
{
[ProtoMember(1)]
public string Name { get; private set; }
[ProtoMember(2)]
public int Ammo { get; private set; }
[ProtoMember(3)]
public string Position { get; private set; }
private Player() { }
public Player(string name, int ammo, string position)
{
this.Name = name;
this.Ammo = ammo;
this.Position = position;
}
}
Client (посылает один объект Player):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NetworkCommsDotNet;
using ProtoBuf;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Player player = new Player("MarcF", 100, "09.09N,21.12W");
//Could also use UDPConnection.GetConnection...
TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000)).SendObject("PlayerData", player);
Console.WriteLine("Send completed. Press any key to exit client.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
Сервер:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NetworkCommsDotNet;
using ProtoBuf;
namespace Server
{
class Program
{
static void Main(string[] args)
{
// Convert incoming data to a <Player> object and run this method when an incoming packet is received.
NetworkComms.AppendGlobalIncomingPacketHandler<Player>("PlayerData", (packetHeader, connection, incomingPlayer) =>
{
Console.WriteLine("Received player data. Player name was " + incomingPlayer.Name);
//Do anything else with the player object here
//e.g. UpdatePlayerPosition(incomingPlayer);
});
//Listen for incoming connections
TCPConnection.StartListening(true);
Console.WriteLine("Server ready. Press any key to shutdown server.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
Вышеупомянутая версия является модифицированной версией этого tutorial. Вам, очевидно, нужно будет загрузить NetworkCommsDotNet DLL с веб-сайта, чтобы вы могли добавить его в ссылку «using NetworkCommsDotNet». Также см. IP-адрес сервера в примере клиента в настоящее время «127.0.0.1», это должно работать, если вы запустили сервер и клиент на том же компьютере.
Хм. Интересно. –
Мой личный быстрый и чистый раствор, используя Json.NET:
class JMessage
{
public Type Type { get; set; }
public JToken Value { get; set; }
public static JMessage FromValue<T>(T value)
{
return new JMessage { Type = typeof(T), Value = JToken.FromObject(value) };
}
public static string Serialize(JMessage message)
{
return JToken.FromObject(message).ToString();
}
public static JMessage Deserialize(string data)
{
return JToken.Parse(data).ToObject<JMessage>();
}
}
Теперь вы можете сериализовать ваши объекты, как так :
Player player = ...;
Enemy enemy = ...;
string data1 = JMessage.Serialize(JMessage.FromValue(player));
string data2 = JMessage.Serialize(JMessage.FromValue(enemy));
Отправить эти данные по проводам, а затем на другом конце вы можете сделать что-то вроде:
string data = ...;
JMessage message = JMessage.Deserialize(data);
if (message.Type == typeof(Player))
{
Player player = message.Value.ToObject<Player>();
}
else if (message.Type == typeof(Enemy))
{
Enemy enemy = message.Value.ToObject<Enemy>();
}
//etc...
Все эти решения очень интересны, но единственная проблема, о которой я могу думать, - это выяснить, какой тип это на стороне приемника? Если я десериализую что-нибудь, смогу ли я получить тип для трансляции? Спасибо за все ответы! Очень полезно. –
Метод, который я изложил, решает именно вашу проблему, а именно, как определить тип. Посмотрите, как я говорю 'message.Type == typeof (Player)' или 'message.Type == typeof (Enemy)' в примере? Вот как вы «извлекаете тип для трансляции». –
После более чем двух лет я нашел новые способы решения этой проблемы, и я думал, что обмен может быть кому-то полезен. Обратите внимание, что принятый ответ по-прежнему действителен.
Простейший способ сериализации типизированных объектов, которые я смог найти, - это использовать json-конвертер в Json.NET. Существует объект настроек, который позволяет хранить тип в json как значение с именем $type
. Вот как это сделать, и в результате JSON:
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
JsonConvert.SerializeObject(myObject, settings);
Json результат:
{
"$type" : "Testing.MyType, Testing",
"ExampleProperty" : "Hello world!"
}
При десериализации, если используется тот же параметр, объект соответствующего типа будет десериализации. Именно то, что мне нужно! Надеюсь это поможет.
- 1. Java-сокеты TCP отправлять и получать
- 2. Отправить объекты через Сокеты
- 3. Может только получать или отправлять через TCP
- 4. последовательная связь через сокеты TCP/UDP
- 5. Сокеты и TCP-сокеты
- 6. Как отправлять/получать двоичные данные через сокеты?
- 7. Сокеты TCP не могут отправлять сообщения в пакет
- 8. Сокеты TCP и WEB
- 9. , получающий переменный размер данных через сокеты TCP
- 10. Как отправить список через TCP-сокеты - Python
- 11. Получите большой список через TCP-сокеты - Python
- 12. , соединяющий два устройства Android через TCP-сокеты
- 13. Отправка файла через TCP-сокеты в Python
- 14. Android: потоковое аудио через TCP-сокеты
- 15. Сокеты - отправлять и получать
- 16. Как отправлять объекты через python?
- 17. Асинхронные сокеты TCP - уточнение?
- 18. C# отправлять объекты структуры через сокет
- 19. TCP-сокеты - InvalidOperationException
- 20. Отправка данных через сокеты tcp. Правильно ли это выглядит?
- 21. Сокеты Erlang TCP закрываются
- 22. Передача данных через сокеты tcp с помощью nodejs
- 23. TCP-сокеты: Двойные сообщения
- 24. Сокеты и соединения TCP
- 25. Сокеты UDP TCP C
- 26. PHP розетки/сокеты TCP
- 27. TCP сокеты в с
- 28. Сокеты домена PostgreSQL UNIX vs TCP-сокеты
- 29. Можно ли отправлять дейтаграммы через TCP?
- 30. Отправлять команды для контролеров OTA через tcp
Я бы сказал, что вам нужно создать собственное решение, высокопроизводительный механизм сериализации. Что-то вдоль линии бит-упаковки будет достаточно быстрым для небольших фрагментов информации. – Machinarius
Поддерживает ли XNA WCF? Если так, то это будет путь. –