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;
	else
		cout << "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");
		else
			printf("unknown\n");
	} else
		printf("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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章