How to Send C++ Struct Message from C# Socket?
This is the C++ Message that i need to pass from C#:
struct LoginMessage:public开发者_Python百科 NMessage { char szUser[ 16 ]; char szPass[ 16 ]; LoginMessage() { msgId = CTRL_SESSION_LOGIN; msgSize = sizeof( LoginMessage ); } }; struct NMessage { DWORD msgSize; union { DWORD msgId; struct { BYTE msgId0; BYTE msgId1; BYTE msgId2; BYTE msgId3; }; }; NMessage() { } BOOL IsLegal() { return msgSize>=sizeof(NMessage) && msgSize
What is the equivalent of this in C# so that C++ can understand this message? Sample code is greatly appreciated.
To answer my own question..
[StructLayout(LayoutKind.Sequential)]
public struct LoginMessage
{
public int msgSize;
public int msgId;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
public string szUser;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 16)]
public string szPass;
}
It is important to take note of the sequence of the class properties so that the byte array you send to c++ is exactly the same as what it expects.
Have a look at this. it's a nice marshaling guid.
Take a look at the StructLayout attribute on MSDN. There is a good example on that page on how to create a struct that can be marshalled correctly. You can also look at http://www.pinvoke.net to see different examples of how the Windows API structs are defined to be marshaled. Once you have the struct defined right, you should be able to use Marshal.StructureToPtr or Marshal.PtrToStructure. Here's a short article on it: http://geekswithblogs.net/taylorrich/archive/2006/08/21/88665.aspx
I usually manually serialize data using something like this.
class NMessage {
byte[] buffer;
//ctor to create a new message for sending.
public NMessage(int nSize) {
buffer = new byte[nSize];
Buffer.BlockCopy(BitConverter.GetBytes(nSize), 0, buffer, 0, sizeof(UInt32));
}
//ctor to create msg from received data.
publix NMessage(byte[] buffer) {
this.buffer = buffer;
}
public UInt32 MessageId {
get { return BitConverter.ToUInt32(buffer, 4);
set { Buffer.BlockCopy(BitConverter.GetBytes(value), 0, buffer, 4, sizeof(UInt32)); }
}
...
public Byte MessageId2 {
get { return buffer[6]; }
set { buffer[6] = value; }
}
...
public UInt32 Size {
get { return BitConverter.ToUInt32(buffer, 0) }
}
public Byte[] Buffer {
get { return buffer; }
}
}
class LoginMessage : NMessage {
Encoding encoding = new ASCIIEncoding(); //or whatever encoding you need.
public LoginMessage() : base(16 + 16) {
this.MessageId = CTRL_SESSION_LOGIN;
}
public LoginMessage(NMessage message) : base(message.Buffer) {
}
public string User {
get { return encoding.GetString(buffer, 8, 16); }
set { Buffer.BlockCopy(encoding.GetBytes(value), 0, buffer, 8, 16);
}
public string Pass {
get { return encoding.GetString(buffer, 24, 16); }
set { Buffer.BlockCopy(encoding.GetBytes(value), 0, buffer, 24, 16);
}
}
So you simply create you new message, set it's data, then send the Buffer property.
Send((new LoginMessage {
User = "user",
Pass = "pass",
}).Buffer);
On the receiving end, if you have enough data, you can build the message from the byte[] you receive.
byte[] recvBuf = new byte[MAX_RECV];
int recvSize = Receive(recvBuf);
...
var message = new NMessage(recvBuf);
if (message.MessageId == CTRL_SESSION_LOGIN)
var login = new LoginMessage(message);
var user = login.User;
...
That's just a rough overview anyway, the code obviously needs a cleanup as I reduced it to only demonstrate the idea.
精彩评论