Splitting up UDP packet

2019-04-12 23:06发布

问题:

I'm using UdpClient to query game servers about server name, map, number of players, etc.

I've followed the guidelines on this page (A2S_INFO) http://developer.valvesoftware.com/wiki/Server_queries#Source_servers

and I'm getting a correct reply:

alt text http://data.fuskbugg.se/skalman01/reply.JPG

I have no idea how I would go about to get each chunk of information (server name, map and the like).

Any help? I'm assuming one would have to look at the reply format specified in the wiki I linked, but I don't know what to make of it.

回答1:

The reply format gives you the order and type of fields in the reply packet, basically like a struct. You can use a class like BinaryReader to read groups of bytes in the packet and interpret them as the appropriate data types.

How are you getting the response?

  1. If it's in a stream already, you're set.
  2. If it's in a byte array, wrap it in a MemoryStream first. I think UdpClient does it this way.

Then, construct a BinaryReader using the stream. Remember, both the stream and the reader need to be Disposed of.

BinaryReader reader = new BinaryReader(stream);

You can now call the reader's methods like ReadByte, ReadInt32 etc. to read each field in turn from the response, using methods corresponding to the fields' types. The stream updates its internal offset as it's read, so you automatically read successive fields from the right place in the response buffer. BinaryReader already has methods appropriate to the five non-string types used in the Steam packets:

  1. byte: ReadByte
  2. short: ReadInt16
  3. long: ReadInt32
  4. float: ReadSingle
  5. long long: ReadUInt64 (yes, there's a U in there; the Valve page says these are unsigned)

string is a bit trickier, because BinaryReader doesn't already have methods to read strings in the format specified by Valve (null-terminated UTF-8), so you'll have to do it yourself, byte by byte. To make it look as much like any other BinaryReader method as possible, you could write an extension method (untested code; this is the gist of it):

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());
}

Some example usage, with the response packet you gave:

// 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.

You can use similar code for creating your request packets, using a BinaryWriter instead of manually assembling individual byte values.



标签: c# udp packet