單純的IPv4地址是個32位的整數,正好用一個ULONG存放,子網掩碼也是如此,這沒問題。可是存在大小端的情況,很多時候我們還需要使用點分十進制的格式,子網掩碼還有進行長度轉換的需求,如255.255.0.0 -> 16,這樣一來就複雜了。
不論如何,我們還是想使用較簡單的辦法,使用一個ULONG,在這裡我們寫作VOS_U32,於是乎我們似乎可以這樣:
typedef VOS_U32 IPADDRESS;
typedef VOS_U32 MASK;
但是不如這樣:
typedef tag_stIpConfig
{
VOS_U32 u32IpAddress;
VOS_U32 u32Mask;
}STIP_CONFIG, * PSTIP_CONFIG;
一個配置對。
當我們需要把IP地址或掩碼的其中一個字節拿出來使用時,有點不方便。在轉換成點分十進制時經常要這麼操作。
感謝有人發明了聯合這種奇妙的數據結構,正好可以派上用場。所以如下方法還真巧妙:
//IP地址
typedef union tag_unIpAddress
{
struct tag_stIpAddress
{
VOS_U8 u8Byte0;
VOS_U8 u8Byte1;
VOS_U8 u8Byte2;
VOS_U8 u8Byte3;
}stBytes;
VOS_U32 u32Value;
}UNIPADDRESS, * PUNIPADDRESS;
//IP配置
typedef tag_stIpConfig
{
UNIPADDRESS unIpAddress;
UNIPADDRESS unMask;
}STIP_CONFIG, * PSTIP_CONFIG;
如此一來,我們在使用時可以直接拿出其32位值或者其中的一個字節,比如這麼干:
STIP_CONFIG stIpConfig;
stIpConfig.unIpAddress.u32Value = 0xC0A80001; //192.168.0.1
stIpConfig.unMask.u32Value = 0xFFFF0000; //255.255.0.0
stIpConfig.unIpAddress.stBytes.u8Byte1 = XX;
stIpConfig.unIpAddress.stBytes.u8Byte2 = XX;
這樣的好處是u32Value和那四個字節是同一個東西,它們是聯動的。
下面我們要來思考大小端的問題。
一個形如1.2.3.4的IP地址是這樣的:0x01020304,當然它也可以是這樣0x04030201,取決於小端還是大端,這真煩人。我們看看在內存里是啥樣兒的。
小端機器上這樣存儲:
+------+ 內存地址
| 0x01 | 0x00000003
| 0x02 | 0x00000002
| 0x03 | 0x00000001
| 0x04 | 0x00000000
+------+
表現在那個聯合里:
+------+ 內存地址 成員
| 0x01 | 0x00000003 u8Byte3
| 0x02 | 0x00000002 u8Byte2
| 0x03 | 0x00000001 u8Byte1
| 0x04 | 0x00000000 u8Byte0
+------+
而大端字節序正好相反,如此複雜,不如我們規定:不論大端還是小端,u8Byte3都是IP地址的最高字節,u8Byte0是最低字節。
例如IP地址1.2.3.4,就得是這樣:
u8Byte0 = 0x04
u8Byte1 = 0x03
u8Byte2 = 0x02
u8Byte3 = 0x01
那麼如上結構經過改造應該如此:
//IP地址
typedef union tag_unIpAddress
{
struct tag_stIpAddress
{
#ifdef _LITTLE_ENDIAN_
VOS_U8 u8Byte0;
VOS_U8 u8Byte1;
VOS_U8 u8Byte2;
VOS_U8 u8Byte3;
#else //大端
VOS_U8 u8Byte3;
VOS_U8 u8Byte2;
VOS_U8 u8Byte1;
VOS_U8 u8Byte0;
#endif
}stBytes;
VOS_U32 u32Value;
}UNIPADDRESS, * PUNIPADDRESS, UNMASK, * PUNMASK;
//IP配置
typedef tag_stIpConfig
{
UNIPADDRESS unIpAddress;
UNMASK unMask;
}STIP_CONFIG, * PSTIP_CONFIG;
挺好。
再來討論子網掩碼的長度轉換問題。
掩碼就是IP地址,存儲時也用上述IP地址的結構。掩碼還可以用長度來表示,這樣比較簡便。我們這裡不考慮“不連續的掩碼”。
事實上一個形如255.0.0.0的掩碼應該是這樣的
255.0.0.0 = 0xFF000000 = 1111 1111, 0000 0000, 0000 0000, 0000 0000。那麼從高位數有8個1,所以掩碼長度就是8。
那麼怎麼做轉換呢?我貼一個我的VVRP里的函數供大家參考。
//
IP_RETCODE IP_MaskIpAddressToLen_H(VOS_IN const UNMASK unMask,
VOS_OUT VOS_U8 * const pu8MaskLen)
{
VOS_CHK_NULL(pu8MaskLen)
{
return IP_FAILED;
}
//無論如何,組裝成1111 1111, 1111 1111, 0000 0000, 0000 0000的形式
VOS_U32 u32MaskTmp = ((unMask.stBytes.u8Byte3) << 24) +
((unMask.stBytes.u8Byte2) << 16) +
((unMask.stBytes.u8Byte1) << 8) +
(unMask.stBytes.u8Byte0);
VOS_U8 u8Count = 0;
for(; 32 > u8Count; u8Count++)
{
//
if(VOS_BIT_OFF == VOS_BIT_TEST(u32MaskTmp, 32 - u8Count))
{
break;
}
}
*pu8MaskLen = u8Count;
return IP_SUCCESS;
}
#define VOS_BIT_TEST(_value, _bit) (((VOS_BIT_ON << ((_bit) - 1)) & (_value)) >> ((_bit) - 1))
由於UNMASK已經確保了u8Byte3是最高字節,所以後面的故事就都沒有大小端問題了。
這裡再給一個通過掩碼長度產生IP地址類型的掩碼的函數。
//
IP_RETCODE IP_MaskLenToIpAddress_H(VOS_IN const VOS_U8 u8MaskLen,
VOS_OUT PUNMASK punMask)
{
VOS_CHK_NULL(punMask)
{
return IP_FAILED;
}
VOS_U32 u32Mask = VOS_Bit_GenValueByBitOn(u8MaskLen);
punMask->u32Address = u32Mask << (32 - u8MaskLen);
return IP_SUCCESS;
}
//
VOS_U32 VOS_Bit_GenValueByBitOn(VOS_IN const VOS_U8 u8BitLen)
{
if(32 < u8BitLen)
{
return VOS_ZERO;
}
VOS_U32 u32Value = 0;
VOS_U8 u8Count = 0;
for(; u8BitLen > u8Count; u8Count++)
{
u32Value |= (VOS_BIT_ON << u8Count);
}
return u32Value;
}
試一試,能正常工作麼?呵呵!