在HpUnix 的C++近日深感孤独,想找远在Windows上的C#小弟聊聊天,双方决定通过 Socket进行通信。协议是只有他们自己能够了解的内部协议,说白了就是自定义的结构体。^_^
好基友在定义了一个消息体和一个回复消息体,其中都包含一个头结构包含这个消息的序列号、消息的类型、消息休的总长度,我交代的事有无办妥的状态。
C++中header的定义:
struct header
{
ACE_UINT32 totol_length_;
ACE_UINT32 command_id_;
ACE_UINT32 command_status_;
ACE_UINT32 sequence_id_;
header()
{
this->totol_length_ = 0;
this->command_id_ = 0;
this->command_status_ = 0;
this->sequence_id_ = 0;
}
header(const header &rhs)
{
this->totol_length_ = rhs.totol_length_;
this->command_id_ = rhs.command_id_;
this->command_status_ = rhs.command_status_;
this->sequence_id_ = rhs.sequence_id_;
}
header &operator=(const header &rhs)
{
this->totol_length_ = rhs.totol_length_;
this->command_id_ = rhs.command_id_;
this->command_status_ = rhs.command_status_;
this->sequence_id_ = rhs.sequence_id_;
return *this;
}
void dump()
{
ACE_DEBUG ((LM_TRACE,ACE_TEXT("%I%D [%t] <%M> header::dump totol_length_:%d,command_id_:%d,command_status_:%d,sequence_id_:%d\n"),
this->totol_length_,this->command_id_,this->command_status_,this->sequence_id_));
}
};
C#小弟为了和大哥沟通也定义了一个相同的消息体,不过用的是本国的语言。
[StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)]
struct header
{
//[MarshalAs(UnmanagedType.SysUInt, SizeConst = 16)]
//[MarshalAs(UnmanagedType.U4)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(0)]
public UInt32 totol_length_;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(4)]
public UInt32 command_id_;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(8)]
public UInt32 command_status_;
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(12)]
public UInt32 sequence_id_;
//public header()
//{
// totol_length_ = 0;
// command_id_ = 0;
// command_status_ = 0;
// sequence_id_ = 0;
//}
public header(header h)
{
this.totol_length_ = h.totol_length_;
this.command_id_ = h.command_id_;
this.command_status_ = h.command_status_;
this.sequence_id_ = h.sequence_id_;
}
}
关于小哥俩的其他私密消息这里就不公布了,我们要尊重人家的隐私嘛。^_^
双方通过MsgHelper工具类(方法体网上收集的,找不到原作者,如有侵权请告知)
public class MsgHelper
{
/// <summary>
/// 将结构转换为字节数组
/// </summary>
/// <param name="obj">结构对象</param>
/// <returns>字节数组</returns>
public static byte[] StructToBytes(object obj)
{
//得到结构体的大小
int size = Marshal.SizeOf(obj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(obj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
}
//接收的时候需要把字节数组转换成结构
/// <summary>
/// byte数组转结构
/// </summary>
/// <param name="bytes">byte数组</param>
/// <param name="type">结构类型</param>
/// <returns>转换后的结构</returns>
public static object BytesToStruct(byte[] bytes, Type type)
{
//得到结构的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes, 0, structPtr, size);
//将内存空间转换为目标结构
object obj = Marshal.PtrToStructure(structPtr, type);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构
return obj;
}
public static byte[] Struct2Bytes<T>(T obj)
{
int size = Marshal.SizeOf(obj);
byte[] bytes = new byte[size];
IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
Marshal.StructureToPtr(obj, arrPtr, true);
return bytes;
}
public static T Bytes2Struct<T>(byte[] bytes)
{
IntPtr arrPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
return (T)Marshal.PtrToStructure(arrPtr, typeof(T));
}
}
C#小弟俩可以使用Struct2Bytes<T>和Bytes2Struct<T> 或 StructToBytes和BytesToStruct的成对使用来把结构体转成bytes流然后通过socket进行传输,
C++则可以直接把char*的结构转成自定义的消息体。
双方在收发消息的时候可以通过头消息的长度判断消息体是否接收完成来保证消息传输的完整性。
在测试的Windows上述工作方法没有任何问题,这对好基友可以畅所欲言,一解相思之苦。
但是正在在HpUnix上运行C++程序时则程序直接崩溃...
为了这哥俩的坚固的基情,作为塔线人费了九牛二虎之力,排除了各种可能性之后仍然没有头绪...
难道他们就要成为现代版的牛郎和织女了么!
也许是他们的基情感动了上天..,在HpUnix上创建一个结构体,然后将其Dump成字符矩阵。然后将收到的消息的原始字符显示出来(不能转成结构体,这个地方正是坑的所在)。
为便于对比摘出了部分内容,4个为一组表示一个整形数据
C++自定义的结构体:00 00 02 d8 00 00 00 13 00 00 00 00 00 00 00 01 收到的结构体: d8 02 00 00 13 00 00 00 00 00 00 00 01 00 00 00
d8 02 00 00 在windows上表示19,13 00 00 00是18以下为转换方法。不知道您有没有看出头绪,我当时的第一感觉就是我靠这不正好反了吗?也就是说是高端对齐,unix是低端对齐...
//d8,02,00,00<br>//<span>13,00,00,00</span><br>
private int translate(string str1)
{
string[] array = str1.Split(',');
byte[] bytes = new byte[array.Length];
for (int i = 0; i < array.Length; i++)
{
string item = array[i];
bytes[i] = Byte.Parse(item, System.Globalization.NumberStyles.HexNumber);
}
return System.BitConverter.ToInt32(bytes, 0);
}
然后开始找布控制这种排列的方式,最后没找到,索性根据位置自已写程序将它们交换。然后世界就和平了....。(如有好的方法,欢迎告知,跪谢...)
注:
1、上述方式仅发生整型,字符型则表现正常。
2、C#中StructLayout,MarshalAs,UnmanagedType类型均无法控制顺序,小道消息说是CPU架构问题。
3、题外话就是在一个平台上好使,不见得在另一个平台就好使。要摒弃先入为主的概念,用太祖的话要"实事求是"。