本人 做了一個工程.用戶端是C#,服務端是DELPHI7.在實施過程中遇到了多種問題,現在就幾個重要問題總結一下,希望對大家有所協助,少走點彎路.(如果有人不以為然,請一笑而過,切勿留下罵口.)
一、首先是對於不同環境下資料包轉換問題。
1、C#裡可以用STRUCT 封裝資料。如:
[StructLayout(LayoutKind.Sequential)]//注意此處一定要有此語句
public struct Body
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=8)] public string password;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=16)] public string Tel;
public int accid;
}
2、DELPHI中對應的資料包則可以表示如:
TBody=Packed Record //包體定義
password: array[0..7] of char; //密碼
Tel :array[0..15] of char; //手機號
accid: integer; //編號
end;
對於上面的封包,大家都很熟悉。但是在c#中一定注意包中類型的長度。那就是 “位元組對齊 ”
為了能使CPU對變數進行高效快速的訪問,變數的起始地址應該具有某些特性,
即所謂的“對齊”。例如對於4位元組的int類型變數,其起始地址應位於4位元組邊界上,
即起始地址能夠被4整除。變數的對齊規則如下(32位系統):
Type
Alignment
char
在位元組邊界上對齊
short (16-bit)
在雙位元組邊界上對齊
int and long (32-bit)
在4位元組邊界上對齊
float
在4位元組邊界上對齊
double
在8位元組邊界上對齊
structures
單獨考慮結構體的個成員,它們在不同的位元組邊界上對齊。
其中最大的位元組邊界數就是該結構的位元組邊界數。
MSDN原話:Largest alignment requirement of any member
理解結構體的對齊有點撓頭,如果結構體中有結構體成員,
那麼這是一個遞迴的過程。
對齊影響結構體成員在結構體中的位移設編譯器設定的最大對齊位元組邊界數為n,
對於結構體中的某一成員item,它相對於結構首地址的實際位元組對齊數目X應該滿足
以下規則:
X = min(n, sizeof(item))
例如,對於結構體 struct {char a; int b} T;
當位於32位系統,n=8時:
a的位移為0,
b的位移為4,中間填充了3個位元組, b的X為4;
當位於32位系統,n=2時:
a的位移為0,
b的位移為2,中間填充了1個位元組,b的X為2;
結構體的sizeof
設結構體的最後一個成員為LastItem,其相對於結構體首地址的
位移為offset(LastItem),其大小為sizeof(LastItem),結構體的位元組對齊數為N,
則:結構體的sizeof 為: 若offset(LastItem)+ sizeof(LastItem)能夠被N整除,
那麼就是offset(LastItem)+ sizeof(LastItem),否則,在後面填充,
直到能夠被N整除。
例如:32位系統,n=8,
結構體 struct {char a; char b;} T;
struct {char a; int b;} T1;
struct {char a; int b; char c;} T2;
sizeof(T) == 2; N = 1 沒有填充
sizeof(T) == 8; N = 4 中間填充了3位元組
sizeof(T2)==12; N = 4 中間,結尾各填充了3位元組
注意:
1) 對於空結構體,sizeof == 1;因為必須保證結構體的每一個執行個體在記憶體中都
有獨一無二的地址。
2) 結構體的靜態成員不對結構體的大小產生影響,因為靜態變數的儲存位置與
結構體的執行個體地址無關。
例如:
struct {static int I;} T; struct {char a; static int I;} T1;
sizeof(T) == 1; sizeof(T1) == 1;
3) 某些編譯器支援擴充指令設定變數或結構的對齊,如VC,
詳見MSDN(alignment of structures)
並不是要求#pragma pack(8),就一定是每個成員都是8位元組對齊
而是指一群組成員要按照8位元組對齊。
struct s1
{
short a; // 2位元組
long b; // 4位元組
};
整個s1小於8位元組,因此s1就是8位元組。
struct s2
{
char c; // 1位元組
s1 d; // 8位元組
__int64 e; // 8位元組
};
整個s2小於12位元組,但是由於#pragma pack(8)的限定,12不能與8位元組對齊,因此s2就是24位元組,c佔用8位元組
總上所述,就是DELPHI的包與C#結構裡的類型長度一定要對應,而且要按照“位元組對齊”原則。