我从服务器的源代码中获得了以下C结构,并且有很多相似的结构:
// preprocessing magic: 1-byte alignment
typedef struct AUTH_LOGON_CHALLENGE_C
{
// 4 byte header
uint8 cmd;
uint8 error;
uint16 size;
// 30 bytes
uint8 gamename[4];
uint8 version1;
uint8 version2;
uint8 version3;
uint16 build;
uint8 platform[4];
uint8 os[4];
uint8 country[4];
uint32 timezone_bias;
uint32 ip;
uint8 I_len;
// I_len bytes
uint8 I[1];
} sAuthLogonChallenge_C;
// usage (the actual code that will read my packets):
sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0]; // where buf is a raw byte array
这些是TCP数据包,我需要实现一些在C#中发出和读取它们的东西。最干净的方法是什么?
我目前的方法涉及
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct foo { ... }
和许多fixed
语句来读取和写入它,但它感觉非常笨重,而且由于数据包本身不是固定长度,我觉得使用它并不舒服。此外,它还有很多工作要做。
然而,它确实很好地描述了数据结构,并且协议可能会随着时间而改变,因此这可能是维护的理想选择。
我有什么选择?用C ++编写它并使用一些.NET魔法来使用它会更容易吗?
澄清:我还需要处理字节序问题和空填充字符串。
答案 0 :(得分:6)
我会创建一个本机C#类来表示数据包及其数据(不是尝试匹配有线格式的数据),并在构造函数中传递BinaryReader
。让它从数据流中以适当的块读取数据:
public class LogonChallenge
{
public LogonChallenge(BinaryReader data)
{
// header
this.Cmd = data.ReadByte();
this.Error = data.ReadByte();
this.Size = data.ReadUInt16();
// etc
}
}
如果您有多个共享公共标头或其他前导字段的数据包类型,则可以使用继承来避免重复。 BasePacket
类可能会读取并填充标题字段,LogonChallenge
类将继承BasePacket
并在调用基础构造函数后开始读取质询字段。
答案 1 :(得分:1)
如果有很多不安全的代码,我可能会考虑用C ++编写代码。可能作为C ++ COM DLL,如果需要,可以很容易地从C#中调用,只需确保COM接口很容易与.Net类型匹配。虽然可能有一些更好的方法使用Managed C ++,我从未使用过。
答案 2 :(得分:1)
同意ho1,我会编写包含此结构的小型C ++ / CLI类。这个类可能需要一个接口,它可以从字节数组填充结构,并为每个结构成员填充属性。 C#客户端可以从从套接字接收的字节数组构造此类实例,并从中读取每个结构成员作为托管属性。所有durty工作都可以在非托管代码中完成。
答案 3 :(得分:0)
好的,这就是我想出的:
abstract class Packet
{
protected enum T
{
Byte,
UInt16,
UInt32,
NullPaddedAsciiString,
Whatever
}
protected struct Offset
{
public int offset;
public T type; // included only for readability
public Offset(int i, T type)
{
this.type = type;
offset = i;
}
}
protected byte[] data;
byte[] RawData { get { return data; } }
// getters and setters will be implemented using something like this
protected UInt16 GetUInt16(Offset o)
{
// magic
}
protected void Write(Offset o, string s)
{
// magic
}
}
class cAuthLogonChallenge : Packet
{
// still not perfect, but at least communicates the intent
static Offset cmd = new Offset(0, T.Byte);
static Offset error = new Offset(1, T.Byte);
static Offset size = new Offset(2, T.UInt16);
// etc.
public cAuthLogonChallenge(string username)
{
var size = 30 + username.Length
data = new byte[size];
Write(cmd, 0x00);
// etc.
}
}