Разделение UDP-пакетов - PullRequest
2 голосов
/ 10 июня 2010

Я использую UdpClient для запроса игровых серверов об имени сервера, карте, количестве игроков и т. Д.

Я следовал инструкциям на этой странице (A2S_INFO) http://developer.valvesoftware.com/wiki/Server_queries#Source_servers

и я получаю правильный ответ:

альтернативный текст http://data.fuskbugg.se/skalman01/reply.JPG

Я понятия не имею, как мне получить каждый кусок информации (имя сервера, карта и т. п.).

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

1 Ответ

6 голосов
/ 11 июня 2010

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

Как вы получаете ответ?

  1. Если он уже в потоке, вы настроены.
  2. Если он находится в байтовом массиве, сначала оберните его в MemoryStream. Я думаю UdpClient делает это так.

Затем создайте BinaryReader, используя поток. Помните, что поток и читатель должны быть Disposed из.

BinaryReader reader = new BinaryReader(stream);

Теперь вы можете вызывать методы читателя, такие как ReadByte, ReadInt32 и т. Д., Чтобы читать каждое поле по очереди из ответа, используя методы, соответствующие типам полей. Поток обновляет свое внутреннее смещение по мере чтения, поэтому вы автоматически читаете последовательные поля из нужного места в буфере ответов. BinaryReader уже имеет методы, соответствующие пяти нестроковым типам, используемым в пакетах Steam:

  1. byte: ReadByte
  2. short: ReadInt16
  3. long: ReadInt32
  4. float: ReadSingle
  5. long long: ReadUInt64 (да, там есть буква U; на странице Valve написано, что они не подписаны)

string немного сложнее, потому что BinaryReader еще не имеет методов для чтения строк в формате, заданном Valve (UTF-8 с нулевым символом в конце), поэтому вам придется делать это самостоятельно, байт байтом Чтобы он выглядел так же, как и любой другой метод BinaryReader, вы можете написать метод расширения (непроверенный код; это суть):

public static string ReadSteamString(this BinaryReader reader)
{
  // To hold the list of bytes making up the string
  List<byte> str = new List<byte>();
  byte nextByte = reader.ReadByte();
  // Read up to and including the null terminator...
  while (nextByte != 0)
  {
    // ...but don't include it in the string
    str.Add(nextByte);
    nextByte = reader.ReadByte();
  }

  // Interpret the result as a UTF-8 sequence      
  return Encoding.UTF8.GetString(str.ToArray());
}

Пример использования с пакетом ответа, который вы дали:

// Returns -1, corresponding to FF FF FF FF
int header = reader.ReadInt32();
// Returns 0x49, for packet type
byte packetType = reader.ReadByte();
// Returns 15, for version
byte ver = reader.ReadByte();
// Returns "Lokalen TF2 #03 All maps | Vanilla"
string serverName = reader.ReadSteamString();
// Returns "cp_well"
string mapName = reader.ReadSteamString();
// etc.

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

...