PHP套接字程式設計(socket) 阿新 • • 發佈:2019-01-26 套接字程式設計,一般使用c或c++。特別的在web應用程式開發中,常用perl實現套接字。除此以外,用php進行套接字程式設計也是一個選擇。Php可以勝任嗎?當然可以。Php是一門高質量的web應用程式開發語言,他的許多特性可以處理眾多的任務,網路程式設計也不例外。 1. 理解套接字 Mail、ftp、telnet、name和finger這些服務都是在一個專用的公開的埠上提供的,通過連線到這些埠,客戶程式就能夠訪問這些服務。這與現實生活是相似的——當需要乾洗衣服的時候,找乾洗店;當需要取錢的時候,去銀行,等等。除了專用於特定伺服器的埠外,計算機還有其它的埠讓程式設計師建立他們自己的伺服器。 埠一般是編號的,通過指定伺服器的埠號,客戶程式可以連線到該埠上。每種伺服器或埠要有特定的協議,為了讓客戶的請求能夠被理解和響應,客戶必須以這種伺服器特有的方式形成客戶請求。 Socket是網路上執行的兩個程式間雙向通訊連線的一端。Socket這個詞的一般意義是自然的或人工的插口,如家用電器的電源插口等。 客戶程式可以向Socket寫請求,伺服器將處理此請求,然後通過Socket把結果返回給客戶。 Socket是一種底層連線。客戶機和伺服器通過寫入到Socket的位元組流進行通訊。它們必須有共同的協議,也就是說,通過Socket相互傳送資訊時所用的語言必須是協定好的。 2. Socket建立連線的過程 建立過程如下:(connection-oriented) server 方過程 client 方過程 socket() socket() | | bind() bind() | | listen() | | | accept()<------------------connect() | | recv()/send() <----------> send()/recv() 3. Php 基本套接字呼叫: 3.1. 基本套接字呼叫 建立套接字--socket(); 繫結本機埠--bind(); 建立連線--connect(),accept(); 偵聽埠--listen(); 資料傳輸--send(),recv(); 輸入/輸出多路複用--select(); 關閉套接字--closesocket() 3.2. php提供的套接字呼叫: 接受連線-—accept connect() 繫結埠—bind () 關閉套接字—close() 初始化連線—connect() 偵聽埠—listen() 讀取套接字—read() 建立套接字—socket() 寫套接字—write() 4. 基本應用 4.1. 一個簡單的TCP伺服器 1 #!/usr/local/bin/php -q 2 3 <?php 4 /* 5 * We don't want any time-limit for how the long can hang 6 * around, waiting for connections: 7 */ 8 set_time_limit(0); 9 10 /* Create a new socket: */ 11 if( ($sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) 12 { 13 print strerror( $sock ) . "n"; 14 exit(1); 15 } 16 17 /* Bind the socket to an address and a port: */ 18 if( ($ret = bind( $sock, "10.31.172.77", 10000 )) < 0 ) 19 { 20 print strerror( $ret ) . "n"; 21 exit(1); 22 } 23 24 /* 25 * Listen for incoming connections on $sock. 26 * The '5' means that we allow 5 queued connections. 27 */ 28 if( ($ret = listen( $sock, 5 )) < 0 ) 29 { 30 print strerror( $ret ) . "n"; 31 } 32 33 /* Accept incoming connections: */ 34 if( ($msgsock = accept_connect( $sock )) < 0) 35 { 36 print strerror( $msgsock ) . "n"; 37 exit(1); 38 } 39 40 /* Send the welcome-message: */ 41 $message = "Welcome to my TCP-server!n"; 42 if( ($ret = write( $msgsock, $message, strlen($message)) ) < 0 ) 43 { 44 print strerror( $msgsock ) . "n"; 45 exit(1); 46 } 47 48 /* Read/Receive some data from the client: */ 49 $buf = ''; 50 if( ($ret = read( $msgsock, $buf, 128 )) < 0 ) 51 { 52 print strerror( $ret ) . "n"; 53 exit(1); 54 } 55 56 /* Echo the received data back to the client: */ 57 if( ($ret = write( $msgsock, "You said: $bufn", strlen("You said: $bufn")) ) < 0 ) 58 { 59 print strerror( $ret ) . "n"; 60 exit(1); 61 } 62 63 /* Close the communication-socket: */ 64 close( $msgsock ); 65 66 /* Close the global socket: */ 67 close( $sock ); 68 ?> 第8行:使用set_time_limit設定程式執行時間為無限以等待連線; 11-15: 建立一個套接字; 18-22: 把建立的套接字與IP及埠繫結; 28-31: 偵聽埠; 34-38: 接受連線; 41-46: 顯示歡迎資訊; 49-54: 讀取客戶端資訊; 57-61: 向客戶端回顯資訊; 63-67: 關閉套接字 4.2. TCP伺服器的執行 上邊這個tcp伺服器的執行要求php編譯成cgi解釋方式,並且編譯時加入--enable-sockets。 如果你已經編譯成cgi解釋方式執行,但是使用命令php -m列出的專案沒有sockets,則說明你需要重新編譯php。當這些要求達到後你就可以執行這個伺服器了 啟動伺服器: ./filename.php 然後就可以使用telnet登入了。 telnet 10.31.172.77 10000 你的終端上將顯示: Trying 10.31.172.77... Connected to 10.31.172.77. Escape character is '^]'. Welcome to my TCP server! 然後輸入一些東西,並回車: Hello You said: Hello Connection closed by foreign host 你也可以修改一下這個程式,讓它像phpmanual上的那個例子,只有當客戶端輸入“quit“的時候才關閉連線。 5. 其他應用 5.1. 聊天室應用 5.1.1. 常見的聊天室實現 一般的聊天室的實現常使用的方法是使用框架頁面,然後對其中一個用於顯示談話內容的框架使用html的方式重新整理,例如: <meta http-equiv=“refresh” content=”3;http://www.jite.net”> 使用這種方式會導致瀏覽器端不斷的向伺服器端發出請求,當有大量的請求時就會使得伺服器執行效率降低。這樣的聊天室顯然是有設計弊端的。 但是如果使用socket的方式實現聊天室,情況就不同了。 5.1.2. 使用socket實現聊天室 我們要討論的聊天室非常簡單,只是一個原理上的實現。 它是一個 client/server 結構的程式, 首先啟動 server, 然後使用者使用 client 進行連線. client/server 結構的優點是速度快, 缺點是當 server 進行更新時, client 也必需更新. 初始化 server, 使server 進入監聽狀態: (以下只是實現原理,並不涉及具體程式) $socket = socket( AF_INET,SOCK_STREAM, 0); // 首先建立一個 socket, 族為 AF_INET, 型別為 SOCK_STREAM. // AF_INET = ARPA Internet protocols 即使用 TCP/IP 協議族 // SOCK_STREAM 型別提供了順序的, 可靠的, 基於位元組流的全雙工連線. // 由於該協議族中只有一個協議, 因此第三個引數為 0 bind ($sock, $address, $port) // 再將這個 socket 與某個地址進行繫結. listen( sockfd, MAX_CLIENT) // 地址繫結之後, server 進入監聽狀態. // MAX_CLIENT 是可以同時建立連線的 client 總數. server 進入 listen 狀態後, 等待 client 建立連線。 Client端要建立連線首先也需要初始化連線: $socket= socket( AF_INET,SOCK_STREAM,0)) // 同樣的, client 也先建立一個 socket, 其引數與 server 相同. connect ($socket, $address, $service_port) // client 使用 connect 建立一個連線. 當 client 建立新連線的請求被送到Server端時, server 使用 accept 來接受該連線: accept_connect($sock) // accept 返回一個新的檔案描述符. 在 server 進入 listen 狀態之後, 由於可能有多個使用者請求連線,所以程式需要同時對這些使用者進行操作,並在它們之間實現資訊交換。這在實現上稱為I/O多路複用技術。 I/O多路複用技術的方法就不是本文所要敘述的內容了,如有興趣請參考相關書籍。 5.2. 一個基於web的新聞組瀏覽器 在php中可以使用fsockopen開啟一個tcp socket連線 int fsockopen (string hostname, int port [, int errno [, string errstr [, double timeout]]]) 有關此函式的使用請參考php手冊。 訪問新聞組服務,需要使用一個協議叫NNTP,即Network News Transfer Protocol。 這個協議有一個專用的RFC描述,它位於 http://www.w3.org/Protocols/rfc977/rfc977.html。 該文件詳細的說明了如何同一個nntp伺服器對話及如何使用命令完成任務。 5.2.1. 連線一個伺服器 <?php $cfgServer = "news.php.net"; $cfgPort = 119; $cfgTimeOut = 10; // open a socket if(!$cfgTimeOut) // without timeout $usenet_handle = fsockopen($cfgServer, $cfgPort); else // with timeout $usenet_handle = fsockopen($cfgServer, $cfgPort, &$errno, &$errstr, $cfgTimeOut); if(!$usenet_handle) { echo "Connexion failedn"; exit(); } else { echo "Connectedn"; $tmp = fgets($usenet_handle, 1024); } ?> 5.2.2. 同伺服器進行對話 在前面,我們已經同伺服器連線上了,假如我們要從某一新聞組中選取10條最近的新聞,該怎麼辦呢? RFC977指出,選擇一個新聞組使用group命令: GROUP ggg <?php //$cfgUser = "xxxxxx"; //$cfgPasswd = "yyyyyy"; $cfgNewsGroup = "alt.php"; // identification required on private server if($cfgUser) { fputs($usenet_handle, "AUTHINFO USER ".$cfgUser."n"); $tmp = fgets($usenet_handle, 1024); fputs($usenet_handle, "AUTHINFO PASS ".$cfgPasswd."n"); $tmp = fgets($usenet_handle, 1024); // check error if($tmp != "281 Okrn") { echo "502 Authentication errorn"; exit(); } } // select newsgroup fputs($usenet_handle, "GROUP ".$cfgNewsGroup."n"); $tmp = fgets($usenet_handle, 1024); if($tmp == "480 Authentication required for commandrn") { echo "$tmpn"; exit(); } $info = split(" ", $tmp); $first = $info[2]; $last = $info[3]; print "First : $firstn"; print "Last : $lastn"; ?> 5.2.3. 讀取新聞 讀取新聞的命令是article,具體用法請參考RFC977,這裡就不提供例程了。 6. 後記 我以為上次寫了一篇,這次就可以免了。離交稿日期沒幾天了,於榮賦來約稿。程稿倉促,難免有錯,請見諒,並且指出。 7. 參考文獻: 廖斌,《php的守護程式程式設計》; w3c,《RFC977》; Daniel Solin,Introduction to Socket Programming with PHP;