網路與通訊端

網路位元組序與 htons/ntohl(network byte order)

/ htons -> aitch-tee-en-ESS, ntohl -> en-tee-oh-ELL /

不同的電腦對於多位元組數字該先寫哪一端,意見不一。有些電腦把 0x12345678 這個數字從 0x12 開始存(大端序),有些從 0x78 開始存(小端序)。在單一機器內這沒問題,但在網路上,兩台意見不同的機器會把彼此的數字讀反。所以網際網路替線路上傳輸的數字挑了一個慣例——大端序——稱為網路位元組序,並給你一些小函式來與它互相轉換。

規則是:任何你放進封包標頭的多位元組數字——最重要的是連接埠號碼與 IP 位址——在送出前必須是網路位元組序(大端序),收進來時則要轉回你機器的順序(主機位元組序)。轉換函式以大小與方向命名:htons() = host-to-network-short(16 位元,用於連接埠),htonl() = host-to-network-long(32 位元,用於 IPv4 位址),而 ntohs()/ntohl() 做相反方向,network-to-host。在大端序機器上這些是無動作;在小端序機器上(像多數 x86 筆電)則會交換位元組。兩種情況你都照樣盲目地呼叫,你的程式碼在兩者上都保持正確。

為何重要:忘了這件事,你的程式碼可能在兩台完全相同的機器之間運作得無懈可擊,然後在和另一台不同的機器交談時莫名失敗——一個沒經 htons() 就送出的連接埠 80,會抵達成連接埠 20480。這是通訊端程式設計中少數你必須有意識地轉換的地方之一,因為這些數字越過了「可能以不同方式存放位元組的機器」之間的邊界。修正方法很機械:在送出時把每個連接埠與位址數字包進 htons()/htonl(),收進來時包進 ntohs()/ntohl()。

addr.sin_port = htons(8080); 把連接埠 8080 從你機器的順序轉成線路上的大端序。回程時 int port = ntohs(addr.sin_port); 把它轉回來,好讓你列印或比較。即使你不確定也照樣呼叫——在大端序主機上它們是正確的無動作。

送出時用 htons/htonl、收進時用 ntohs/ntohl——機械化,而且在每台機器上都正確。

IP 的位元組字串、或你訊息酬載的位元組並不是數字,不可經過這些函式——只有像連接埠與 IPv4 位址這類多位元組整數標頭欄位才需要轉換。

又稱
big-endian on the wirebyte order conversion網路位元組序大端序