【數據結構】IP地址配置存儲探究

單純的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;
}


試一試,能正常工作麼?呵呵!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值