程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> PHP套接字編程

PHP套接字編程

編輯:PHP綜合

套接字編程,一般使用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 方過程
[code]
socket() socket()
| |
bind() bind()
| |
listen() |
| |
accept()<------------------connect()
| |
recv()/send() <----------> send()/recv()

[/code]
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服務器
[code]
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: $buf\n", strlen("You said: $buf\n")) ) < 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 ?>
[/code]
第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 進入監聽狀態: (以下只是實現原理,並不涉及具體程序)
[code]
$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 返回一個新的文件描述符.

[/code]
在 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描述,它位於 [url=http://www.w3.org/Protocols/rfc977/rfc977.html。]http://www.w3.org/Protocols/rfc977/rfc977.html。[/url]
該文檔詳細的說明了如何同一個nntp服務器對話及如何使用命令完成任務。
5.2.1. 連接一個服務器
[code]
<?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 failed\n";
exit();
}
else {
echo "Connected\n";
$tmp = fgets($usenet_handle, 1024);
}
?>

[/code]
5.2.2. 同服務器進行對話
在前面,我們已經同服務器連接上了,假如我們要從某一新聞組中選取10條最近的新聞,該怎麼辦呢?
RFC977指出,選擇一個新聞組使用group命令:
[code]
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 Ok\r\n") {
echo "502 Authentication error\n";
exit();
}
}

// select newsgroup

fputs($usenet_handle, "GROUP ".$cfgNewsGroup."\n");
$tmp = fgets($usenet_handle, 1024);

if($tmp == "480 Authentication required for command\r\n") {
echo "$tmp\n";
exit();
}

$info = split(" ", $tmp);
$first = $info[2];
$last = $info[3];

print "First : $first\n";
print "Last : $last\n";
?>

[/code]
5.2.3. 讀取新聞
讀取新聞的命令是article,具體用法請參考RFC977,這裡就不提供例程了。

6. 後記
我以為上次寫了一篇,這次就可以免了。離交稿日期沒幾天了,於榮賦來約稿。程稿倉促,難免有錯,請見諒,並且指出。

7. 參考文獻:
廖斌,《php的守護程序編程》;
w3c,《RFC977》;
Daniel Solin,Introduction to Socket Programming with PHP;

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved