1. 程式人生 > >網路程式設計——3. 地址族與資料序列

網路程式設計——3. 地址族與資料序列

3.1 給套接字分配IP地址與埠號

IP(Internet Protocol)是指網路協議,是為收發網路資料而分配給計算機的值 埠號是為區分程式中建立的套接字而分配給套接字的序號

網路地址

IP地址分為兩類:

IPv4 IPv6
4位元組地址族 6位元組地址族

IPv4標準的4位元組IP地址分為網路地址和主機地址,且分為A、B、C、D、E等型別在這裡插入圖片描述

網路ID是為了區分網路而設定的一部分IP地址。 在傳輸資料時,並非一開始就瀏覽所有4位元組IP地址,而是僅瀏覽4位元組IP地址中的網路地址,找到網路,把資料傳到構成網路的路由器,待路由器接到資料後,瀏覽傳輸資料的主機地址(主機ID)並將資料傳給目標計算機

結論:“向相應的網路傳輸資料”,實際上是向構成網路的路由器或交換機傳遞資料,由接收資料的路由器根據資料中的主機地址向目標主機傳遞資料

網路地址分類與主機地址邊界

A類地址的首位元組範圍:0 - 127 B類地址的首位元組範圍:128 - 191 C類地址的首位元組範圍:192 - 223 除此以外,還有 A類地址的首位以0開始 B類地址的首位以10開始 C類地址的首位以110開始

用於區分套接字的埠號

同一作業系統內通過埠號把資料傳輸給相應埠的套接字。 無法將一個埠號分配給不同的套接字。 埠號由16位構成,可分配的埠號範圍是0 - 65535,但0 - 1023是知名埠,一般分配給特定的應用程式。 TCP套接字和UDP套接字不會共用埠號,允許重複。

3.2 地址資訊的表示

表示IPv4地址的結構體

該結構體將作為地址資訊傳遞給bind函式

struct sockaddr_in {
	sa_family_t sin_family;	// 地址族
	uint16_t sin_port;	// 16位TCP/UDP埠號
	struct in_addr sin_addr;// 32位IP地址
	char sin_zero[8];	// 不使用
};

該結構體中另一結構體定義如下,用來存放32位的IP地址

struct in_addr {
	In_addr_t s_addr;	// 32位IPv4地址
};

3.3 網路位元組序與地址變換

不同CPU中,4位元組整形值1在記憶體空間的儲存方式是不同的。 4位元組整型值1用二進位制表示為 00000000 00000000 00000000 00000001 有些CPU以這種順序儲存到記憶體,而另一些會以倒序儲存 00000001 00000000 00000000 00000000 所以,一定要注意,儲存順序的不同意味著對接收資料的解析順序也不同

位元組序和網路位元組序

CPU向記憶體儲存資料的方式有兩種,意味著CPU解析資料的方式也分兩種: 大端序:高位位元組存放到低位地址,高位先存 小端序:高位位元組存放到高位地址,低位先存

舉例說明:對於4位元組int型別數0x12345678,儲存在0x20號開始的地址中 在這裡插入圖片描述

目前主流的Intel系列CPU以小端序方式儲存 約定網路位元組序統一為大端序,即先把資料化成大端序格式再進行網路傳輸

位元組序轉換

幫助轉換位元組序的函式, h代表主機(host)位元組序,n代表網路(network)位元組序 s代表short,l代表long(linux中long型別佔用4個位元組)

  • unsigned short htons(unsigned short);
  • unsigned short ntohs(unsigned short);
  • unsigned long htonl(unsigned long);
  • unsigned long ntohl(unsigned long);

可以理解為把short型別的資料從網路位元組序轉為主機位元組序 通常,s代表2個位元組short,用於埠號轉換;l代表4個位元組,用於IP地址轉換

3.4 網路地址的初始化與分配

將字串資訊轉換為網路位元組序的整數型

sockaddr_in中儲存地址資訊的成員為32位整形。因此要將熟悉的IP地址點分十進位制表示法轉換成32位整型資料 在這裡插入圖片描述 該函式還可以檢測無效的IP地址。

char *addr1 = "1.2.3.4";
char *addr2 = "1.2.3.256";
unsigned long conv_addr = inet_addr(addr1);
printf("Network ordered integer addr: %#lx \n", conv_addr); 
conv_addr = inet_addr(addr2);
printf("Network ordered integer addr: %#lx \n", conv_addr); 

第一個輸出0x4030201,確實轉換為網路位元組序 第二個為錯誤的IP地址。

相反的,下面的函式可以把網路位元組序整型IP地址轉換成點分十進位制形式 注意返回值型別為char指標 在這裡插入圖片描述

網路地址初始化

struct sockaddr_in addr;
char *serv_ip = "211.217.168.13";	
char *serv_port = "9190";

// 結構體變數addr的所有成員初始化為0,最後一個引數為傳入addr的長度
memset(&addr,0,sizeof(addr));
// 指定地址族
serv_addr.sin_family = AF_INET;
// 基於字串的IP地址初始化
serv_addr.sin_addr.s_addr = inet_addr(serv_ip);
// 基於字串的埠號初始化
serv_addr.sin_port = htons(atoi(serv_port));

INADDR_ANY

利用常數INADDR_ANY分配伺服器端的IP地址。可自動獲取執行伺服器端的計算機IP地址。 若同一計算機中已分配多個IP地址(如路由器),只要埠號一致,就可以從不同IP地址接收資料。 伺服器端中優先考慮這種方式

struct sockaddr_in addr;
char *serv_port = "9190";

// 結構體變數addr的所有成員初始化為0,最後一個引數為傳入addr的長度
memset(&addr,0,sizeof(addr));
// 指定地址族
serv_addr.sin_family = AF_INET;
// 基於字串的IP地址初始化
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 基於字串的埠號初始化
serv_addr.sin_port = htons(atoi(serv_port));

向套接字分配網路地址

在sockaddr_in結構體初始化以後,就可以把初始化的地址資訊分配給套接字了 在這裡插入圖片描述