網路程式設計——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結構體初始化以後,就可以把初始化的地址資訊分配給套接字了