這篇文章主要介紹了PHP SOCKET編程詳解,需要的朋友可以參考下
1. 預備知識
一直以來很少看到有多少人使用php的socket模塊來做一些事情,大概大家都把它定位在腳本語言的范疇內吧,但是其實php的socket模塊可以做很多事情,包括做ftplist,http post提交,smtp提交,組包並進行特殊報文的交互(如smpp協議),whois查詢。這些都是比較常見的查詢。
特別是php的socket擴展庫可以做的事情簡直不會比c差多少。
php的socket連接函數
1、集成於內核的socket
這個系列的函數僅僅只能做主動連接無法實現端口監聽相關的功能。而且在4.3.0之前所有socket連接只能工作在阻塞模式下。
此系列函數包括
fsockopen,pfsockopen
這兩個函數的具體信息可以查詢php.net的用戶手冊
他們均會返回一個資源編號對於這個資源可以使用幾乎所有對文件操作的函數對其進行操作如fgets(),fwrite(), fclose()等單注意的是所有函數遵循這些函數面對網絡信息流時的規律,例如:
fread() 從文件指針 handle 讀取最多 length 個字節。 該函數在讀取完 length 個字節數,或到達 EOF 的時候,或(對於網絡流)當一個包可用時就會停止讀取文件,視乎先碰到哪種情況。
可以看出對於網絡流就必須注意取到的是一個完整的包就停止。
2、php擴展模塊帶有的socket功能。
php4.x 以後有這麼一個模塊extension=php_sockets.dll,Linux上是一個extension=php_sockets.so。
當打開這個此模塊以後就意味著php擁有了強大的socket功能,包括listen端口,阻塞及非阻塞模式的切換,multi-client 交互式處理等
這個系列的函數列表參看http://www.php.net/manual/en/ref.sockets.php
看過這個列表覺得是不是非常豐富呢?不過非常遺憾這個模塊還非常年輕還有很多地方不成熟,相關的參考文檔也非常少:(
我也正在研究中,因此暫時不具體討論它,僅給大家一個參考文章
http://www.zend.com/pecl/tutorials/sockets.php
2. 使用PHP socket擴展
服務器端代碼:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <?php /** * File name server.php * 服務器端代碼 * * @author guisu.huang * @since 2012-04-11 * */ //確保在連接客戶端時不會超時 set_time_limit(0); //設置IP和端口號 $address = "127.0.0.1"; $port = 2046; //調試的時候,可以多換端口來測試程序! /** * 創建一個SOCKET * AF_INET=是ipv4 如果用ipv6,則參數為 AF_INET6 * SOCK_STREAM為socket的tcp類型,如果是UDP則使用SOCK_DGRAM */ $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() 失敗的原因是:" . socket_strerror(socket_last_error()) . "/n"); //阻塞模式 socket_set_block($sock) or die("socket_set_block() 失敗的原因是:" . socket_strerror(socket_last_error()) . "/n"); //綁定到socket端口 $result = socket_bind($sock, $address, $port) or die("socket_bind() 失敗的原因是:" . socket_strerror(socket_last_error()) . "/n"); //開始監聽 $result = socket_listen($sock, 4) or die("socket_listen() 失敗的原因是:" . socket_strerror(socket_last_error()) . "/n"); echo "OKnBinding the socket on $address:$port ... "; echo "OKnNow ready to accept connections.nListening on the socket ... n"; do { // never stop the daemon //它接收連接請求並調用一個子連接Socket來處理客戶端和服務器間的信息 $msgsock = socket_accept($sock) or die("socket_accept() failed: reason: " . socket_strerror(socket_last_error()) . "/n"); //讀取客戶端數據 echo "Read client data n"; //socket_read函數會一直讀取客戶端數據,直到遇見n,t或者 字符.PHP腳本把這寫字符看做是輸入的結束符. $buf = socket_read($msgsock, 8192); echo "Received msg: $buf n"; //數據傳送 向客戶端寫入返回結果 $msg = "welcome n"; socket_write($msgsock, $msg, strlen($msg)) or die("socket_write() failed: reason: " . socket_strerror(socket_last_error()) ."/n"); //一旦輸出被返回到客戶端,父/子socket都應通過socket_close($msgsock)函數來終止 socket_close($msgsock); } while (true); socket_close($sock);客戶端代碼:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php /** * File name:client.php * 客戶端代碼 * * @author guisu.huang * @since 2012-04-11 */ set_time_limit(0); $host = "127.0.0.1"; $port = 2046; $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)or die("Could not create socketn"); // 創建一個Socket $connection = socket_connect($socket, $host, $port) or die("Could not connet servern"); // 連接 socket_write($socket, "hello socket") or die("Write failedn"); // 數據傳送 向服務器發送消息 while ($buff = socket_read($socket, 1024, PHP_NORMAL_READ)) { echo("Response was:" . $buff . "n"); } socket_close($socket);使用cli方式啟動server:
php server.php
這裡注意socket_read函數:
可選的類型參數是一個命名的常數:
PHP_BINARY_READ - 使用系統recv()函數。用於讀取二進制數據的安全。 (在PHP>“默認= 4.1.0)
PHP_NORMAL_READ - 讀停在 n或r(在PHP <= 4.0.6默認)
針對參數PHP_NORMAL_READ ,如果服務器的響應結果沒有 n。造成socket_read(): unable to read from socket
3. PHP socket內部源碼
從PHP內部源碼來看,PHP提供的socket編程是在socket,bind,listen等函數外添加了一個層,讓其更加簡單和方便調用。但是一些業務邏輯的程序還是需要程序員自己去實現。
下面我們以socket_create的源碼實現來說明PHP的內部實現。
前面我們有說到php的socket是以擴展的方式實現的。在源碼的ext目錄,我們找到sockets目錄。這個目錄存放了PHP對於socket的實現。直接搜索PHP_FUNCTION(socket_create),在sockets.c文件中找到了此函數的實現。如下所示代碼:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21