1. 程式人生 > >socket 套接字程式設計筆記——IP地址轉換

socket 套接字程式設計筆記——IP地址轉換

0.前言

    網上有很多使用arduion和樹莓派連線yeelink的例子,硬體和軟體的實現方式都非常簡單。通過學習這些例子一下激發我學習嵌入式網路的動力。雖然使用arduion連線yeelink簡單方便穩定可靠,但是依然像使用嵌入式乙太網協議棧連線yeelink,例如MCU使用STM32,網絡卡晶片使用ENC28J60,乙太網協議棧使用LwIP。雖然這樣做硬體軟體都要複雜的多,但是也多了不少“樂趣”。事情總是要循序漸進,我決定先認真研究socket程式設計,使用PC平臺和yeelink交換資料。

1.測試環境說明

    windows環境,編譯器為minGW,IDE為eclipse。windows環境下的套接字程式設計和linux環境略有區別,但是基本的思路和方法相同。若使用minGW加eclipse的開發方式,需要加入wsock32庫。新增的方法如下:
1.project -> properties 2.c/c++ build -> settting 3.tool setting  -> mingw c linker -> libraries 4.add wsock32

圖1 新增wsock32庫

2.IP地址格式轉換

    通常情況下,IP地址都被寫成以下格式:192.168.1.101或者10.13.11.105。這種形式的IP地址易於理解,但是對於協議處理來說就顯得不是那麼的方便,為了讓IP地址更容易被處理併兼顧網路傳輸中的格式(網路傳輸為大端格式),所以定義了in_addr結構體:
struct in_addr {
	union {
		struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
		struct { u_short s_w1,s_w2; } S_un_w;
		u_long S_addr;
	} S_un;
#define s_addr  S_un.S_addr
#define s_host  S_un.S_un_b.s_b2
#define s_net   S_un.S_un_b.s_b1
#define s_imp   S_un.S_un_w.s_w2
#define s_impno S_un.S_un_b.s_b4
#define s_lh    S_un.S_un_b.s_b3
};

    在一些網上流傳的套接字程式碼中,經常會看到這樣的程式碼
struct in_addr server_addr server_addr.s_addr = .....     此處的server_addr為一個in_addr型別結構體,server_addr代表一個IP地址。如果理解in_addr型別結構體,也就是把一個IP地址理解為4個字元,或2個16位長度整數,或1個32位長度的整數。由於in_addr型別結構體中包含一個共用體。為使程式設計更簡便些可使用s_addr替代S_un.S_addr,所以便有了server_addr.s_addr這樣的程式碼。in_addr型別結構體一般不單獨出現(除了DNS地址解析外),而是存在於sockaddr_in結構體中,sockaddr_in可理解為套接字地址結構體。

3.格式之間的相互轉換

    ASCII形式的IP地址可以和整數形式的IP地址相互相關,在windows平臺下可以使用inet_addr和inet_ntoa。

3.1 inet_addr

    函式原型和輸入輸出引數
unsigned long inet_addr(
  _In_  const char *cp
);
    函式作用
    把ASCII格式的IP地址(AAA.BBB.CCC.DDD形式)轉換為一個32位無符號整數。

3.2 inet_ntoa

    函式原型和輸入輸出引數
char* FAR inet_ntoa(
  _In_  struct   in_addr in
);
    函式作用
    把in_addr型別的IP地址轉換為ASCII格式的IP地址(AAA.BBB.CCC.DDD形式)。inet_ntoa中的n可理解為network,a可理解為ascii。該函式一般用於列印IP地址。

4.示例程式碼

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>

int main(int argc, char **argv)
{
    unsigned long addr = INADDR_NONE;

    // 測試inet_addr
    char server_ipaddr[] = {"192.168.1.101"};
    addr = inet_addr( server_ipaddr);
    if ( addr == INADDR_NONE && addr == INADDR_ANY )
    {
        printf("轉換失敗\n");
        return 1;
    }   
    // 轉換結果(unsigned long)1694607552
    printf("轉換結果(unsigned long)%lu\n",addr);

    // 測試inet_itoa
    // 轉化為ASCII字串形式的IP地址
    struct in_addr client_ipaddr;
    client_ipaddr.s_addr = addr;

    // 轉換結果("XXX.XXX.XXX.XXX") 192.168.1.101
    printf( "轉換結果(\"XXX.XXX.XXX.XXX\") %s" , inet_ntoa( client_ipaddr ) );

    return 0;
}

執行結果 轉換結果(unsigned long)1694607552 轉換結果("XXX.XXX.XXX.XXX") 192.168.1.101 程式分析     首先準備一個ASCII形式的IP地址,例如192.168.1.101。使用inet_addr轉換為一個32位無符號整數,轉換結果為1694607552。保留該結果並在通過inet_ntoa還原192.168.1.101。還原之前需要先定義in_addr型別結構體,並命名為client_ipaddr,client_ipaddr.s_addr 為32位無符號整數可直接賦值。最後使用inet_ntoa對client_ipaddr變數進行格式轉換並通過串列埠列印。