1. 程式人生 > >《TCP/IP網路程式設計》第3章 筆記&程式碼&註釋

《TCP/IP網路程式設計》第3章 筆記&程式碼&註釋

IP(網路協議Internet Protocol):為了收發網路資料而給計算機分配的值。

埠號:為了區分程式中建立的套接字而分配給套接字的序號。

網路地址

  • IPv4 4位元組地址族(目前主要使用)
  • IPv6 16位元組地址族(為了應對IP地址耗盡而提出的標準,但現在仍未普及)

IPv4

IPv4標準的4位元組地址分為網路地址主機(計算機)地址,且分為A,B,C,D,E型別,E類為已預約地址一般不會使用。

  1位元組 1位元組 1位元組 1位元組 首位元組範圍
A類 網路ID 主機ID 主機ID 主機ID 0~127:1.0.0.1—126.155.255.254
B類 網路ID 網路ID 網路ID 主機ID 128~191:128.0.0.1—191.255.255.254
C類 網路ID 網路ID 網路ID 主機ID 192~223:192.0.0.1—223.255.255.254
D類 網路ID 網路ID 網路ID 網路ID

(D類不分網路和主機地址,前四位固定為1110)

224.0.0.1—239.255.255.254

埠號(port)

埠號用於區分套接字:當資料傳輸到主機上時,但計算機並不知道資料用於哪一個程式(如何分配套接字)。

埠號由16為組成,可分配埠號為0~65535,但0~1023位知名埠號,一般不使用。

TCP與UDP不會共用埠號:TCP使用了2333埠,UDP仍可使用2333埠,但其他TCP套接字就不可以使用2333埠。

資料傳輸

瀏覽IP地址的網路地址 → 將資料傳到該網路地址的路由器 → 路由器接收資料後,根據主機地址尋找目標計算機 → 計算機收到資料後,根據埠號

將資料進行分配

地址資訊的表示

SOCKADDR_IN servAddr;//windows
struct sockaddr_in serv_addr;//Linux
......
bind(ServerSock,(SOCKADDR*)&servAddr,sizeof(servAddr));//windows
bind(serv_sock,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//Linux

在使用bind()填寫地址資訊可以看到,servAddr是SOCKADDR_IN資料型別,在SOCKADDR_IN內,他儲存了:

//Linux
struct sockaddr_in {
    short            sin_family;       // 2 bytes e.g. AF_INET, AF_INET6
    unsigned short   sin_port;    // 2 bytes e.g. htons(3490)
    struct in_addr   sin_addr;     // 4 bytes see struct in_addr, below
    char             sin_zero[8];     // 8 bytes zero this if you want to use
};

sockaddr用其餘14個位元組來表示sa_data,而sockaddr_in把14個位元組拆分成sin_port, sin_addr和sin_zero.

sockaddr和sockaddr_in包含的資料都是一樣的,但他們在使用上有區別:程式設計師不應操作sockaddr,sockaddr是給作業系統用的,因此我們會在向bind()傳參時,對SOCKADDR_IN(sockaddr_in)進行強制型別轉換。

網路位元組序

  • 大端序 高位位元組存放到低位地址
  • 小端序 高位位元組存放到高位地址

當資料傳輸並進行解析後,因為計算機布偶聽的位元組序會導致不同的情況:

00000000 00000000 00000000 00000001(大端序)

00000001 00000000 00000000 00000000 (小端序)

位元組序轉換

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

從函式名稱理解,如:htonl→ 將long整型資料主機位元組序(host)轉換為網路位元組序

為了保證資料的準確傳輸與解析,必須保證收發機器都為相同的大端序(或將資料進行轉換處理)

htons(),htonl()函式(windows)

這兩個函式在Linux上並無區別:

unsigned short host_port= 0x1234;
unsigned short net_port;
unsigned long host_addr=0x12345678;
unsigned long net_addr;
//.....
net_port=htons(host_port);//net_port=0x3412
net_addr=htonl(host_addr);//net_addr=0x78563412;

inet_addr()函式(wiodows)

const char* addr="127.212.124.78";
unsigned long conv_addr=inet_addr(addr);//0x4e7cd47f

函式將IP地址轉換為32位整數型,且可以檢測無效的IP地址。

inet_ntoa()函式(wiodows)

struct sockaddr_in addr
char* strPtr;

addr.sin_addr.s_addr=htonl(0x1020304);
strPtr = inet_ntoa(addr.sin_addr);//1.2.3.4

將一個32位網路位元組序的二進位制IP地址轉換成相應的點分十進位制的IP地址(返回點分十進位制的字串在靜態記憶體中的指標)。

程式碼:

https://github.com/ChristmasError/TCP-IP-Network-programming/tree/master/%E7%AC%AC%E4%B8%89%E7%AB%A0%20%E5%9F%BA%E4%BA%8Ewindows%E7%9A%84%E5%AE%9E%E7%8E%B0

網路地址初始化

//windows
#define ser_port 8888
SOCKADDR_IN servAddr;
char *ser_ip="211.217.168.13"
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;//地址族 IPv4
servAddr.sin_addr.s_addr = htonl(ser_ip);//基於字串的IP地址初始化
servAddr.sin_port = htons(ser_port);//基於整形資料的埠號初始化

以上程式碼是windows內常見的網路地址初始化方法。

INADDR_ANY

//....
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
//....

在對ip地址初始化,我們可以使用INADDR_ANY,採用這種方式,會自動獲取執行伺服器端的計算機的IP地址而不必自己輸入,優先考慮這種方式(除非客戶端中帶有一部分伺服器端功能)。

在這裡, 127.0.0.1是回送地址(loopback address),指計算機自身的IP地址,第一章中,伺服器端和客戶端在同一機器上執行,因此都為127.0.0.1。