ip(點分十進位制 二進位制整數)之間的轉換
linux的套接字部分比較容易混亂,在這裡稍微總結一下。
地址轉換函式在地址的文字表示式和它們存放在套接字地址結構中的二進位制值進行轉換。
地址轉換函式有四個:其中inet_addr 和 inet_ntoa適用於IPv4,inet_pton 和 inet_ntop同時適於用IPv4和IPv6。
套接字地址結構分為IPv4套接字地址結構sockaddr_in和IPv6套接字地址結構sockaddr_in6。其中IPv4的套接字地址結構如下。
IPv4套接字地址結構:(定義在
struct in_addr{
in_addr_t s_addr; //
};
struct sockaddr_in{
uint8_t sin_len;
sa_family_t sin_family; //套接字地址結構的地址族
in_port_t sin_port //TCP或UDP埠,一般為uint16_t
struct in_addr sin_addr; //IPv4地址,一般為uint32_t
char sin_zero[8];
};
-->說明:POSIX規範只需要這個結構中的3個欄位:sin_family、sin_addr和sin_port。對於符合POSIX的實現來說,定義額外的結構欄位是可以接受的。幾乎所有的實現都增加了sin_zero欄位,所以所有的套接字地址結構大小都至少是16位元組。
-->IPv4地址和TCP或UDP埠號在套接字地址結構中總是以網路位元組序來儲存,在使用這些欄位時,必須要牢記這一點。
-->32位IPv4地址存在兩種不同的訪問方法。例如:如果serv第一位某個網際套接字地址結構,那麼serv.sin_addr將按in_addr結構引用其中的32位IPv4地址,而serv.sin_addr.s_addr將按in_addr_t(通常是一個uint32_t)引用同一個32位Ipv4地址。在將它作為函式的引數時要注意使用正確的IPv4地址,因為編譯器對傳遞結構和傳遞正數的處理是完全不同的。
-->sin_zero欄位未曾使用,不過在填寫這種套接字地址結構時,總是把該欄位置為0(總是在填寫前把整個結構置為0)
-->套接字地址結構僅在給定主機上使用:雖然結構中的某些欄位(例如IP地址和埠號)用在不同主機之間的通訊,但是結構本身並不在主機之間傳遞。
通用套接字地址結構:
-->當作為一個引數傳遞進任何套接字函式時,套接字地址結構總是以引用形式(也就是以指向該結構的指標)來傳遞。然而以這樣的指標作為引數之一的任何套接字函式必須處理來自所有支援的任何協議族的套接字地址結構。在如何宣告所傳遞指標的資料型別上存在一個問題,有了ANSI C後解決辦法:void *。然而在ANSI C之前的解決辦法是在
struct sockaddr{
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
於是套接字函式被定義為以指向某個通用套接字地址結構的一個指標作為其引數之一,例如bind函式的ANSI C函式原型:
int bind(int, struct sockaddr *, socklen_t); //一般是uint32_t
這就要求對這些函式的任何呼叫都必須要將指向特定於協議的套接字地址結構的指標進行強制型別轉換,變成指向某個通用套接字地址型別的指標,如:
struct sockaddr_in serv;
bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));
值–結果引數
當向一個套接字函式傳遞一個套接字地址結構時,該結構總是以引用形式來傳遞,也就是說傳遞的是一個指向該結構的一個指標。該結構的長度也作為一個引數來傳遞,但是其傳遞方式取決於該結構的傳遞方向:程序–>核心,核心–>程序
從程序到核心傳遞套接字地址結構的函式有3個:bind,connect,sendto。這些函式的一個引數是指向某個套接字地址結構的指標,另一個引數是該結構的整數大小,例如:
struct sockaddr_in serv;
connect(sockdf, (SA *) &serv, sizof(serv));
從核心到程序傳遞套接字地址結構的函式有4個:accept, recvfrom,getsockname和getpeername。這4個函式的其中兩個引數是指向某個套接字地址結構的指標和指向表示該結構大小的整數的指標,如:
struct sockaddr_un cli;
socklen_t len;
len = sizeof(cli);
getpeername(unixfd, (SA *) &cli, &len);
把套接字地址結構大小這個引數從一個整數改為指向某個整數變數的指標,其原因在於:當單數被呼叫時,結構大小是一個值,它告訴核心結構的大小,這樣核心在寫該結構時不至於越界;當函式返回時,結構大小又是一個結果,它告訴程序核心在該結構中究竟儲存了多少資訊。這種型別的引數成為值-結果引數。
網路位元組序 <–> 主機位元組序:有四個函式用於網路位元組序和主機位元組序之間的轉換:
以下兩個均返回網路位元組序
uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);
以下兩個均返回主機位元組序
uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohs(uint32_t net32bitvalue);
地址轉換函式(兩組):
inet_aton, inet_addr, inet_ntoa:這一組函式在點分十進位制字串(“206.168.112.96”)與它長度為32位的網路位元組序二進位制值間轉換IPv4地址。原型如下:
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr); //返回:若字串有效則為1,否則為0
in_addr_t inet_addr(const char *strptr); //返回:若字串有效則為32位二進位制網路位元組序的IPv4地址,否則為INADDR_NONE
char *inet_ntoa(struct in_addr inaddr); //返回:指向一個點分十進位制數串的指標
//還有一個名為inet_network的函式和inet_addr類似,但是其返回的是主機位元組序
in_addr_t inet_network(const char *strptr);
inet_aton 和 inet_addr 兩個函式都是將一個點分十進位制字串轉換成一個32位的網路位元組序二進位制值。inet_aton函式,如果addrptr指標為空,那麼該函式仍然對輸入的字串執行有效性檢查,但是不儲存任何結果。inet_addr函式存在一些問題:所有2^32個可能的二進位制值都是有效的ip地址(0.0.0.0-255.255.255.255),但是當出錯時函式返回INADDR_NONE常量(通常是一個32位均為1的值),這意味著點分十進位制數串255.255.255.255不能由該函式處理,因為它的二進位制用來指示該函式失敗,inet_addr還存在一個潛在的問題:一些手冊宣告該函數出錯時返回-1而不是INADDR_NONE,這樣在對該函式的返回值和一個負常量進行比較時可能會發生問題。如今,inet_addr已經廢棄,新的程式碼應該改用inet_ato函式,更好的解決方法是使用inet_pton函式。另外需要注意的是inet_ntoa函式的引數是in_addr的結構體而不是指標。
inet_pton,inet_ntop這一組函式對IPv4和IPv6地址都適用。原型如下:
#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //返回:若成功則為1,若輸入不是有效的表達格式則為0,若出錯則為-1
const char *inet_ntop(int family, void *addrptr, char *strptr, size_t len); //返回:若成功則為指向結果的指標,若出錯則為NULL
//len:INET_ADDRSTRLEN INET6_ADDRSTRLEN
//inet_ntop函式的strptr不可以是一個空指標,呼叫者必須為目標儲存單元分配記憶體並制定其大小,呼叫成功時,這個指標就是該函式的返回值
inet_pton(AF_INET),inet_aton, inet_addr
點分十進位制數 -----------------------------------------------------> in_addr{}
IPv4地址 <----------------------------------------------------- 32位二進位制IPv4地址
inet_ntop(AF_INET),inet_ntoa
#include <stdio.h>
#include >string.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
const char *ip_str = "127.0.0.1";
char *ip_res;
in_addr_t addr_t;
struct in_addr addr;
//1.str -> binary
inet_aton(ip_str, &addr);
printf("inet_aton::%x\n", addr); //1000007f
addr_t = inet_addr(ip_str);
printf("inet_addr::%x\n", addr_t); //1000007f
inet_pton(AF_INET, ip_str, (void *)&addr); //1000007f
printf("inet_pton::%x\n", addr);
//2.binary -> str
ip_res = inet_ntoa(addr);
printf("inet_ntoa::%s\n", ip_res); //127.0.0.1
inet_ntop(AF_INET, &addr, ip_res, INET_ADDRSTRLEN);
printf("inet_ntop::%s\n", ip_res); //127.0.0.1
return 0;
}
output:
inet_aton::100007f
inet_addr::100007f
inet_pton::100007f
inet_ntoa::127.0.0.1
inet_ntop::127.0.0.1