天天看點

《Windows網絡與通信程式設計(第3版)》——2.2 Winsock的尋址方式和位元組順序include // 完整代碼在配套CD光牒的LocalHostInfo工程下

本節書摘來自異步社群《windows網絡與通信程式設計(第3版)》一書中的第2章,第2.2節,作者: 陳香凝 , 王烨陽 , 陳婷婷 , 張铮 更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

本節講述在winsock中主機位址資訊的表示方法,以及相關的操作函數。

2.2.1 winsock尋址

因為winsock要相容多個協定,是以必須使用通用的尋址方式。tcp/ip使用ip位址和端口号來指定一個位址,但是其他協定也許采用不同的形式。如果winsock強迫使用特定的尋址方式,添加其他協定就不大可能了。winsock的第一個版本使用sockaddr結構來解決此問題。

在這個結構中,第一個成員sa_family指定了這個位址使用的位址家族。sa_data成員存儲的資料在不同的位址家族中可能不同。本書僅僅使用internet位址家族(tcp/ip),winsock已經定義了sockaddr結構的tcp/ip版本——sockaddr_in結構。它們本質上是相同的結構,但是第2個更容易操作。

在winsock中,應用程式通過sockaddr_in結構來指定ip位址和端口号,定義如下。

(1)sin_family域必須設為af_inet,它告訴winsock程式使用的是ip位址家族。

(2)sin_port域指定了tcp或udp通信服務的端口号。應用程式在選擇端口号時必須小心,因為有一些端口号是保留給公共服務使用的,如ftp和http。基本上,端口号可分成如下3個範圍:公共的、注冊的、動态的(或私有的)。

0~1 023由iana(internet assigned numbers authority)管理,保留為公共的服務使用。

1 024~49 151是普通使用者注冊的端口号,由iana列出。

49 152~65 535是動态和/或私有的端口号。

普通使用者應用程式應該選擇1 024~49 151的注冊了的端口号,以避免使用了一個其他應用程式或者系統服務已經使用的端口号。在49 152~65 535之間的端口号也可以自由地使用,因為沒有服務注冊這些端口号。

(3)sin_addr域用來存儲ip位址(32位),它被定義為一個聯合來處理整個32位的值,兩個16位部分或者每個位元組單獨分開。描述32位ip位址的in_addr結構定義如下。

用字元串“aa.bb.cc.dd”表示ip位址時,字元串中由點分開的4個域是以字元串的形式對in_addr結構中的4個u_char值的描述。由于每個位元組的數值範圍是0~255,是以各域的值都不可超過255。

(4)最後一個域sin_zero沒有使用,是為了與sockaddr結構大小相同才設定的。

應用程式可以使用inet_addr函數将一個由小數點分隔的十進制ip位址字元串轉化成由32位二進制數表示的ip位址。inet_ntoa是inet_addr函數的逆函數,它将一個網絡位元組順序的32位ip位址轉化成字元串。

注意,inet_addr傳回的32位二進制數是用網絡順序存儲的,下一小節詳細講述位元組順序。

2.2.2 位元組順序

位元組順序是長度跨越多個位元組的資料被存儲的順序。例如,一個32位的長整型0x12345678跨越4個位元組(每個位元組8位)。intel x86機器使用小尾順序(little-endian),意思是最不重要的位元組首先存儲。是以,資料0x12345678在記憶體中的存放順序是0x78、0x56、0x34、0x12。大多數不使用小尾順序的機器使用大尾順序(big-endian),即最重要的位元組首先存儲。同樣的值在記憶體中的存放順序将是0x12、0x34、0x56、0x78。因為協定資料要在這些機器間傳輸,是以就必須標明其中的一種方式做為标準,否則會引起混淆。

tcp/ip統一規定使用大尾方式傳輸資料,也稱為網絡位元組順序。例如,端口号(它是一個16位的數字)12345(0x3039)的存儲順序是0x30、0x39。32位的ip位址也是以這種方式存儲的,ip位址的4部分存儲在4個位元組中,第一部分存儲在第一個位元組中。

上述sockaddr和sockaddr_in結構中,除了sin_family成員(它不是協定的一部分)外,其他所有值必須以網絡位元組順序存儲。winsock提供了一些函數來處理本地機器的位元組順序和網絡位元組順序的轉換。

這些api是平台無關的。使用它們可以保證程式正确地運作在所有機器上。

下面代碼示例了如何初始化sockaddr_in結構。

2.2.3 擷取位址資訊

通常,主機上的接口被靜态地指定一個ip位址,或者是由配置協定來配置設定,如動态主機配置協定(dhcp)。如果dhcp伺服器不能到達,系統會使用automatic private ip addressing (apipa)自動配置設定169.254.0.0/16範圍内的位址。

1.擷取本機ip位址

擷取本機的ip位址比較簡單,下面的getallips例子列印出了本機使用的所有ip(一個擴充卡一個ip位址),程式代碼如下。

getallips先調用gethostname取得本地主機的名稱,然後通過主機名得到其位址資訊。

2.擷取mac位址

有時為了檢測網絡,或者為了一些其他特殊的目的,需要自己來直接操作原始資料幀(第9章再具體講述),這就需要擷取自己和lan中其他主機的mac位址。

擷取本地機器的mac位址很容易,使用幫助函數getadaptersinfo即可。此函數的作用是擷取本地機器的擴充卡資訊,用法如下。

下面的例子localhostinfo列印出了本機的ip位址、網絡(内部lan)的子網路遮罩、網關的ip位址和本機的mac位址。本書第9章講述網絡掃描與檢測時還要使用本例中的代碼。

調用自定義函數getglobaldata之後,程式運作結果如圖2.1所示。

要取得lan中其他主機的mac位址,最簡單的方法是使用sendarp函數向目标主機發送arp請求封包。這個函數傳回指定的目的ip位址對應的實體位址,即mac位址。第9章再詳細讨論arp,以及sendarp函數的用法。

《Windows網絡與通信程式設計(第3版)》——2.2 Winsock的尋址方式和位元組順序include // 完整代碼在配套CD光牒的LocalHostInfo工程下

繼續閱讀