Socket编程:必须要了解的网络字节序和转换函数
目錄
主機字節序和網絡字節序
轉換函數
htonl 、htons 、ntohl 和 ntohs
inet_addr 、inet_aton 和 inet_ntoa
inet_pton?和 inet_ntop
主機字節序和網絡字節序
現代 CPU 的累加器一次能裝載至少 4 字節,即一個整數。那么這 4個 字節在內存中的排列的順序將影響它被累加器裝載成的整數的值,這就是字節序問題。字節序分為大端字節序?(即大端模式,Big endian) 和?小端字節序?(即小端模式,Little endian)。
大端字節序:整數的高位字節存儲在內存的低地址處,低位字節存儲在內存的高地址處。
小端字節序:整數的高位字節存儲在內存的高地址處,而低位字節存儲在內存的低地址處。
以下兩種方法可以用來驗證你的 PC 是大端模式還是小端模式:
#include <iostream> #include <string> using namespace std;int main() {int as = 0x12345678;char *p =(char *) &as;if(*p == 0x12)cout << "Big-endian" <<endl;elsecout << "Little-endian" <<endl;union {short s;char c[sizeof(short)];} un;un.s = 0x0102;if (sizeof(short) == 2) {if (un.c[0] == 1 && un.c[1] == 2)printf("Big-endian\n");else if (un.c[0] == 2 && un.c[1] == 1)printf("Little-endian\n");elseprintf("unknown\n");} elseprintf("sizeof(short) = %d\n", sizeof(short));return 0; }一個 32 位值的、4 字節以下順序傳輸:首先是0?- 7位,然后是8?- 15位,接著是16?-?23位,最后是24 - 31位。這就是所謂的大端字節序,它是 TCP/IP 頭部中所有二進制整數在網絡中傳輸時所需的字節順序,它也被稱為網絡字節序。計算機的 CPU 使用其他格式存儲二進制整數,例如大多數 PC 使用小端字節序。
當格式化的數據在兩臺使用不同字節序的主機之間直接傳遞時,接收端按自己模式對數據進行提取或解釋時必然會導致錯誤。解決問題的方法是:發送端總是把要發送的數據轉化成大端模式數據后再發送,而接收段知道對方傳送過來的數據總是采用大端模式,所以接收段可以根據自身采用的模式決定是否對接收到的數據進行轉換。
注意:網絡字節序是 TCP/IP 中規定好的一種數據表示格式,它與具體的 CPU?類型、操作系統等無關,它保證數據在不同主機之間傳輸時能夠被正確解釋。
?
?
轉換函數
htonl 、htons 、ntohl 和 ntohs
Linux 提供了如下4個函數來完成主機字節序和網絡字節序之間的轉換:
頭文件#include <netinet/in.h> unsigned long int htonl ( unsigned long int hostlong ); htonl 把unsigned long int類型從主機序轉換到網絡序unsigned short int htons ( unsigned short int hostlong ); htons把unsigned short int類型從主機序轉換到網絡序unsigned long int ntohl ( unsigned long int netlong ); ntohl 把unsigned long int類型從網絡序轉換到主機序 unsigned short int ntohs ( unsigned short int netshort ); ntohs 把unsigned short int類型從網絡序轉換到主機序在這 4 個函數中,長整型函數通常用來轉換 IP 地址,短整型函數用來轉換端口號。當然函數的使用不限于此,任何格式化的數據通過網絡傳輸時,都應該使用這些函數來轉換字節序。
?
inet_addr 、inet_aton 和 inet_ntoa
下面 3 個函數可用于將點分十進制字符串表示的 IPv4 地址和用網絡字節序整數表示的 IPv4 地址之間進行轉換,這里做一個簡單的介紹:
#include<arpa/inet.h> in_addr_t inet_addr( const char *strptr ); int inet_aton( const char * cp, struct in_addr* inp ); char * inet_ntoa( struct in_addr in );inet_addr 函數將用點分十進制字符串表示的 IPv4 地址轉化為用網絡字節序整數表示的 IPv4 地址。它失敗時返回 INADDR_NONE。
inet_aton 函數完成和 inet_addr 同樣的功能,但是將轉化結果存儲于參數 inp 指向的地址結構中。它成功返回 1 ,失敗返回 0。
inet_ntoa 函數將用網絡字節序整數表示的 IPv4 地址轉化為用點分十進制字符串表示的 IPv4 地址。但需要注意的是,該函數內部用一個靜態變量存儲轉化結果,函數的返回值指向該靜態內存,因此 inet_ntoa 是不可重入的。
?
inet_pton?和 inet_ntop
inet_pton?和 inet_ntop也能完成和前面 3 個函數同樣的功能,并且它們同時適用于 IPv4 地址和 IPv6 地址。
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int?inet_pton(int af, const char *src, void *dst);inet_pton?函數將用字符串表示的 IP 地址 src 轉換成用網絡字節序整數表示的 IP 地址,即[點分十進制] -> [二進制整數]。并把轉換結果存儲于 dst 指向的內存中。
參數1:af 指定地址族,可以是 AF_INET 或 AF_INET6。
參數2:src?是來源地址。
參數3:dst?接收轉換后的數據。
若成功,則返回1;如果函數出錯將返回一個負值,并將 errno 設置為 EAFNOSUPPORT;如果參數 af 指定的地址族和?src 格式不對,函數將返回 0。
?
#include <sys/socket.h> #include <netinet/in.h> #include<arpa/inet.h> const?char *inet_ntop(int af, const void *src, char *dst, size_t len);這 inet_ntop 函數將[二進制整數] -> [點分十進制]。
參數1:af?可以是 AF_INET 或 AF_INET6。
參數2:src?是一個指向網絡字節序的二進制值的指針。
參數3:dst?是一個指向轉換后的點分十進制串的指針。
參數4:len?是目標的大小,以免函數溢出其調用者的緩沖區。
若成功,則為指向結果的指針;若出錯,則返回?NULL。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main () {char IPdotdec[20]; //存放點分十進制IP地址struct in_addr s; //IPv4地址結構體//輸入IP地址printf("Please input IP address: ");scanf("%s", IPdotdec);//轉換inet_pton(AF_INET, IPdotdec, (void *)&s);printf("inet_pton: 0x%x\n", s.s_addr); //注意得到的字節序//反轉換inet_ntop(AF_INET, (void *)&s, IPdotdec, 16);printf("inet_ntop: %s\n", IPdotdec); }輸出: Please input IP address: 192.169.1.1 inet_pton: 0x101a9c0 inet_ntop: 192.169.1.1?
?
參考:
百度百科:https://baike.baidu.com/item/inet_pton
https://blog.csdn.net/u010889616/article/details/47157637?utm_source=blogxgwz6
總結
以上是生活随笔為你收集整理的Socket编程:必须要了解的网络字节序和转换函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ string 使用详解(含C++
- 下一篇: C++ priority_queue 的