使用php編寫socket程序時,也需要注意主機字節序和網絡字節序的轉換
主機字節序就是我們平常說的大端和小端模式:不同的 CPU 有不同的字節序類型,這些字節序是指整數在內存中保存的順序 這個叫做主機序。Big-Endian和Little-Endian。引用標準的Big-Endian和Little-Endian的定義如下:
a) Little-Endian就是低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
b) Big-Endian就是高位字節排放在內存的低地址端,低位字節排放在內存的高地址端。
例子:在內存中雙字0x01020304(DWORD)的存儲方式
內存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04
網絡字節順序是指TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操作系統等無關,從而可以保證數據在不同主機之間傳輸時能夠被正確解釋。網絡字節順序採用big endian排序方式。字節序,顧名思義字節的順序,就是大於一個字節類型的數據在內存中的存放順序,一個字節的數據沒有順序的問題了。
爲了進行轉換 bsd socket 提供了轉換的函數 有下面四個
htons 把 unsigned short 類型從主機序轉換到網絡序
htonl 把 unsigned long 類型從主機序轉換到網絡序
ntohs 把 unsigned short 類型從網絡序轉換到主機序
ntohl 把 unsigned long 類型從網絡序轉換到主機序
在使用 little endian 的系統中 這些函數會把字節序進行轉換
在使用 big endian 類型的系統中 這些函數會定義成空宏
注意:
1 、網絡與主機字節轉換函數 :htons ntohs htonl ntohl (s 就是 short l 是 long h 是 host n 是 network)
2 、不同的 CPU 上運行不同的操作系統,字節序也是不同的
在網絡程序開發時 或是跨平臺開發時 也應該注意保證只用一種字節序 不然兩方的解釋不一樣就會產生bug.
PHP中並沒提供htons,htonl功能的函數,在進行網絡通信時,可以通過
string pack ( string $format [, mixed $args [, mixed $... ]] )
array unpack ( string $format , string $data )
進行編解碼
其中,format 可以取下面的值:
Code Description
a NUL-padded string
A SPACE-padded string
h Hex string, low nibble first
H Hex string, high nibble first
c signed char
C unsigned char
s signed short (always 16 bit, machine byte order)
S unsigned short (always 16 bit, machine byte order)
n unsigned short (always 16 bit, big endian byte order)
v unsigned short (always 16 bit, little endian byte order)
i signed integer (machine dependent size and byte order)
I unsigned integer (machine dependent size and byte order)
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
f float (machine dependent size and representation)
d double (machine dependent size and representation)
x NUL byte
X Back up one byte
Z NUL-padded string (new in PHP 5.5)
@ NUL-fill to absolute position
網絡通信
比如現在要通過PHP發送數據包到服務器來登錄。在僅需要提供用戶名(最多30個字節)和密碼(md5之後固定爲32字節)的情況下,可以構造如下數據包(當然這事先需要跟服務器協商好數據包的規範,本例以網絡字節序通信):
包結構:
字段
字節數 說明
包頭 定長
每一個通信消息必須包含的內容
包體 不定長
根據每個通信消息的不同產生變化
其中包頭詳細內容如下:
字段
字節數 類型
說明
pkg_len 2
ushort 整個包的長度,不超過4K
version 1
uchar 通訊協議版本號
command_id 2
ushort 消息命令ID
result 2
short 請求時不起作用;請求返回時使用
當然實際中可能會涉及到各種校驗。本文爲了簡單,只是列舉一下通常的工作流程及處理的方式。
登錄(執行命儲1001)
字段
字節數 類型
說明
用戶名 30
uchar[30] 登錄用戶名
密碼 32
uchar[32] 登錄密碼
包頭是定長的,通過計算可知包頭佔7個字節,並且包頭在包體之前。比如用戶陳一回需要登錄,密碼是123456,則代碼如下:
<?php
$version = 1;
$result = 0;
$command_id = 1001;
$username = "陳一回";
$password = md5("123456");
// 構造包體
$bin_body = pack("a30a32", $username, $password);
// 包體長度
$body_len = strlen($bin_body);
$bin_head = pack("nCns", $body_len, $version, $command_id, $result); //result爲什麼使用了主機字節序
$bin_data = $bin_head . $bin_body;
// 發送數據
// socket_write($socket, $bin_data, strlen($bin_data));
// socket_close($socket);
轉自:http://www.kongqingquan.com/archives/24
參考:http://www.cnblogs.com/andydao/p/4200662.html