PHP中提供了Socket擴展功能,能夠很方便的實現Socket編程。在PHP手冊中有兩段示范代碼,分別實現了TCP/IP的Server和Client。在這兩個例子中,使用Socket發送或接收的都是字符串。眾所周知,在PHP中有很多字符串相關的函數,處理字符串是非常方便的。但是,如果是在PHP中使用Socket函數和另一個程序通信時,不是直接用字符串來傳遞消息,而是用特定的數據結搆比如整型甚至結搆體怎么辦呢?
PHP中變量的類型通常不是由程序員設定的,確切地說,是由PHP根據該變量使用的上下文在運行時決定的。PHP在變量定義中不需要(或不支持)明示的類型定義,變量類型是根據使用該變量的上下文所決定的。也就是說,如果把一個字符串值賦給變量var,var就成了一個字符串。如果又把一個整型值賦給var,那它就成了一個整數。而且PHP中的整型數的字長和平台有關(通常是32 位有符號),不支持無符號整數,那么如果要發送一個32位的無符號整數又怎么來搆造它呢?
看完下面的例子,這些問題就都可以回答了。
例如要在PHP中用Socket發送或者接收一個結搆體:
#define UINT8 unsigned char
#define UINT32 unsigned long
typedef struct _test_struct
{
UINT8 type;
UINT8 name[10];
UINT32 ip;
} test_struct;
1. type是一個字節的無符號字符,可以取值1, 2, 3等。
2. name是一個字符串(字符數組),以’\0’結束,長度為10個字節。
3. ip是一個32位的無符號整型變量,保存IP地址。
4. 為了處理方便,假設結搆體都是按1個字節對齊的(很多C編譯器默認都是按4個字節對齊的),否則要根據對齊方式來計算結搆體成員的偏移。
首先來看一下怎么在PHP中搆造這個結搆體并使用Socket發送出去。搆造的結搆體保存在變量$ buf中。
由于PHP中沒有明確的類型定義,所以只能按字節來搆造這個結搆體,也就是說,只能一個字節一個字節來搆造。
第一個字節是type,可以使用chr函數根據ASCII碼產生一個字節的字符。例如:
$ buf = chr(1);
接着是10個字節的字符串,這個就比較容易了,直接賦值就行,但是要注意的是,如果字符串的長度不夠的話,必須在后面補上’\0’來保證它是10個字節。例如:
$ name = ‘Dennis’;
while (strlen($ name) < 10)
{
$name .= chr(0);
}
$ buf .= $ name;
while (strlen($ name) < 10)
{
$name .= chr(0);
}
$ buf .= $ name;
最后4個字節的無符號整型就比較麻煩了,要將四個字節的數拼在一起。例如:
$ ip = ‘192.168.0.1’;
$ iparr = array();
$ iparr = explode('.', $ ip);
$ buf .= sprintf('%c%c%c%c', $ iparr[0], $ iparr[1], $ iparr[2], $ iparr[3]);
這里用了sprintf來格式化字符串,同樣也可以使用chr函數來處理。$ iparr = array();
$ iparr = explode('.', $ ip);
$ buf .= sprintf('%c%c%c%c', $ iparr[0], $ iparr[1], $ iparr[2], $ iparr[3]);
搆造完這個結搆體就要通過Socket把它發送出去了,以TCP為例,發送代碼如下:
// socket param
$ sockip = ‘192.168.0.254’;
$ sockport = 8000;
// create a udp socket
$ socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($ socket < 0)
{
die("Socket Error!");
}
// connect to UDP port
$ result = socket_connect($ socket, $ sockip, $ sockport);
if ($ result < 0)
{
die("Socket Error!");
}
// send data
$ sent = socket_write($ socket, $ buf, strlen($ buf));
// close socket
socket_close($ socket);
那么如果是接收這個結搆體又怎么處理呢?可能你已經想到了,和發送類似,就是按字節去還原。下面就是具體的方法,假設Socket接受的數據保存在變量$ buf中。這里用到了和chr函數互補的另一個函數ord,它可以獲得字符對應的ASCII碼。
// get type
$ type = ord($ buf{0});
// get name
$ name = substr($ buf, 1, 10);
// get ip
$ ipstr = ord($ buf{14}) . '.' . ord($ buf{13}) . '.' . ord($ buf{12}) . '.' . ord($ buf{11});
上面的代碼獲得的是IP地址的字符串形式,如果要獲得對應的長整型,可以用下面的方法:
$ iplong = (ord($ buf{14}) << 24) + (ord($ buf{13}) << 16) + (ord($ buf{12}) << 8) + ord($ buf{11});
看到這里,上面的問題也都基本上解決了,更復雜的結搆都可以通過這種“逐字節”的方法來處理。雖然之前用PHP開發比較多,但是一直沒用過它來進行Socket編程,當時曾經在網上找過相關的文章,但是沒有找到。上面的這種方法是我自己想出來的,也許不是最好最簡單的實現方法,還是寫出來,希望對大家能從中受益。
沒有留言:
張貼留言