php socket 基础知识
◆ Socket 基礎(chǔ)
PHP使用Berkley的 socket庫來創(chuàng)建它的連接。socket只不過是一個數(shù)據(jù)結(jié)構(gòu)。你使用這個socket數(shù)據(jù)結(jié)構(gòu)去開始一個客戶端和服務器之間的會話。這個服務器是一 直在監(jiān)聽準備產(chǎn)生一個新的會話。當一個客戶端連接服務器,它就打開服務器正在進行監(jiān)聽的一個端口進行會話。這時,服務器端接受客戶端的連接請求,那么就進 行一次循環(huán)?,F(xiàn)在這個客戶端就能夠發(fā)送信息到服務器,服務器也能發(fā)送信息給客戶端。
產(chǎn)生一個Socket,你需要三個變量:一個協(xié)議、一個socket類型和一個公共協(xié)議類型。產(chǎn)生一個socket有三種協(xié)議供選擇,繼續(xù)看下面的內(nèi)容來獲取詳細的協(xié)議內(nèi)容。
定義一個公共的協(xié)議類型是進行連接一個必不可少的元素。下面的表我們看看有那些公共的協(xié)議類型。
表一:協(xié)議
名字/常量???? 描述
AF_INET 這是大多數(shù)用來產(chǎn)生socket的協(xié)議,使用TCP或UDP來傳輸,用在IPv4的地址
AF_INET6???? 與上面類似,不過是來用在IPv6的地址
AF_UNIX 本地協(xié)議,使用在Unix和Linux系統(tǒng)上,它很少使用,一般都是當客戶端和服務器在同一臺機器上的時候使用
表二:Socket類型
名字/常量???? 描述
SOCK_STREAM 這個協(xié)議是按照順序的、可靠的、數(shù)據(jù)完整的基于字節(jié)流的連接。這是一個使用最多的socket類型,這個socket是使用TCP來進行傳輸。
SOCK_DGRAM 這個協(xié)議是無連接的、固定長度的傳輸調(diào)用。該協(xié)議是不可靠的,使用UDP來進行它的連接。
SOCK_SEQPACKET 這個協(xié)議是雙線路的、可靠的連接,發(fā)送固定長度的數(shù)據(jù)包進行傳輸。必須把這個包完整的接受才能進行讀取。
SOCK_RAW 這個socket類型提供單一的網(wǎng)絡(luò)訪問,這個socket類型使用ICMP公共協(xié)議。(ping、traceroute使用該協(xié)議)
SOCK_RDM 這個類型是很少使用的,在大部分的操作系統(tǒng)上沒有實現(xiàn),它是提供給數(shù)據(jù)鏈路層使用,不保證數(shù)據(jù)包順序
表三:公共協(xié)議
名字/常量???? 描述
ICMP 互聯(lián)網(wǎng)控制消息協(xié)議,主要使用在網(wǎng)關(guān)和主機上,用來檢查網(wǎng)絡(luò)狀況和報告錯誤信息
UDP????? 用戶數(shù)據(jù)報文協(xié)議,它是一個無連接,不可靠的傳輸協(xié)議
TCP 傳輸控制協(xié)議,這是一個使用最多的可靠的公共協(xié)議,它能保證數(shù)據(jù)包能夠到達接受者那兒,如果在傳輸過程中發(fā)生錯誤,那么它將重新發(fā)送出錯數(shù)據(jù)包。
現(xiàn) 在你知道了產(chǎn)生一個socket的三個元素,那么我們就在php中使用socket_create()函數(shù)來產(chǎn)生一個socket。這個 socket_create()函數(shù)需要三個參數(shù):一個協(xié)議、一個socket類型、一個公共協(xié)議。socket_create()函數(shù)運行成功返回一個 包含socket的資源類型,如果沒有成功則返回false。
Resourece socket_create(int protocol, int socketType, int commonProtocol);
現(xiàn)在你產(chǎn)生一個socket,然后呢?php提供了幾個操縱socket的函數(shù)。你能夠綁定socket到一個IP,監(jiān)聽一個socket的通信,接受一個socket;現(xiàn)在我們來看一個例子,了解函數(shù)是如何產(chǎn)生、接受和監(jiān)聽一個socket。
<?php
$commonProtocol = getprotobyname(“tcp”);//使用公共協(xié)議名字來獲取一個協(xié)議類型
$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);//產(chǎn)生一個socket并且返回一個socket資源的實例
socket_bind($socket, ‘localhost’, 1337);//綁定socket到本地計算機
socket_listen($socket);//監(jiān)聽所有進來的socket連接
// More socket functionality to come
?>
上面這個例子產(chǎn)生一個你自己的服務器端。例子第一行,
$commonProtocol = getprotobyname(“tcp”);
使 用公共協(xié)議名字來獲取一個協(xié)議類型。在這里使用的是TCP公共協(xié)議,如果你想使用UDP或者ICMP協(xié)議,那么你應該把getprotobyname() 函數(shù)的參數(shù)改為“udp”或“icmp”。還有一個可選的辦法是不使用getprotobyname()函數(shù)而是指定SOL_TCP或SOL_UDP在 socket_create()函數(shù)中。
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
例子的第二行是產(chǎn)生一個socket并且返回一個socket資源的實例。在你有了一個socket資源的實例以后,你就必須把socket綁定到一個IP地址和某一個端口上。
socket_bind($socket, ‘localhost’, 1337);
在這里你綁定socket到本地計算機(127.0.0.1)和綁定socket到你的1337端口。然后你就需要監(jiān)聽所有進來的socket連接。
socket_listen($socket);
在第四行以后,你就需要了解所有的socket函數(shù)和他們的使用。
表四:Socket函數(shù)
函數(shù)名????? 描述
socket_accept()??? 接受一個Socket連接
socket_bind()???? 把socket綁定在一個IP地址和端口上
socket_clear_error()?? 清除socket的錯誤或者最后的錯誤代碼
socket_close()???? 關(guān)閉一個socket資源
socket_connect()??? 開始一個socket連接
socket_create_listen()?? 在指定端口打開一個socket監(jiān)聽
socket_create_pair()?? 產(chǎn)生一對沒有區(qū)別的socket到一個數(shù)組里
socket_create()??? 產(chǎn)生一個socket,相當于產(chǎn)生一個socket的數(shù)據(jù)結(jié)構(gòu)
socket_get_option()??? 獲取socket選項
socket_getpeername()?? 獲取遠程類似主機的ip地址
socket_getsockname()?? 獲取本地socket的ip地址
socket_iovec_add()??? 添加一個新的向量到一個分散/聚合的數(shù)組
socket_iovec_alloc()?? 這個函數(shù)創(chuàng)建一個能夠發(fā)送接收讀寫的iovec數(shù)據(jù)結(jié)構(gòu)
socket_iovec_delete()?? 刪除一個已經(jīng)分配的iovec
socket_iovec_fetch()?? 返回指定的iovec資源的數(shù)據(jù)
socket_iovec_free()??? 釋放一個iovec資源
socket_iovec_set()??? 設(shè)置iovec的數(shù)據(jù)新值
socket_last_error()??? 獲取當前socket的最后錯誤代碼
socket_listen()???? 監(jiān)聽由指定socket的所有連接
socket_read()???? 讀取指定長度的數(shù)據(jù)
socket_readv()???? 讀取從分散/聚合數(shù)組過來的數(shù)據(jù)
socket_recv()???? 從socket里結(jié)束數(shù)據(jù)到緩存
socket_recvfrom()??? 接受數(shù)據(jù)從指定的socket,如果沒有指定則默認當前socket
socket_recvmsg()??? 從iovec里接受消息
socket_select()???? 多路選擇
socket_send()???? 這個函數(shù)發(fā)送數(shù)據(jù)到已連接的socket
socket_sendmsg()??? 發(fā)送消息到socket
socket_sendto()??? 發(fā)送消息到指定地址的socket
socket_set_block()??? 在socket里設(shè)置為塊模式
socket_set_nonblock()?? socket里設(shè)置為非塊模式
socket_set_option()??? 設(shè)置socket選項
socket_shutdown()??? 這個函數(shù)允許你關(guān)閉讀、寫、或者指定的socket
socket_strerror()??? 返回指定錯誤號的詳細錯誤
socket_write()???? 寫數(shù)據(jù)到socket緩存
socket_writev()??? 寫數(shù)據(jù)到分散/聚合數(shù)組
以上所有的函數(shù)都是PHP中關(guān)于socket的,使用這些函數(shù),你必須把你的socket打開,如果你沒有打開,請編輯你的php.ini文件,去掉下面這行前面的注釋:
extension=php_sockets.dll
如果你無法去掉注釋,那么請使用下面的代碼來加載擴展庫:
<?php
if(!extension_loaded(‘sockets’)) {
if(strtoupper(substr(PHP_OS, 3)) == “WIN”) {
dl(‘php_sockets.dll’);
}else{
dl(‘sockets.so’);
}
}
?>
如果你不知道你的socket是否打開,那么你可以使用phpinfo()函數(shù)來確定socket是否打開。你通過查看phpinfo信息了解socket是否打開。
查看phpinfo()關(guān)于socket的信息
◆ 產(chǎn)生一個服務器
現(xiàn)在我們把第一個例子進行完善。你需要監(jiān)聽一個指定的socket并且處理用戶的連接。
<?php
$commonProtocol = getprotobyname("tcp");
$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);
socket_bind($socket, 'localhost', 1337);
socket_listen($socket);
// Accept any incoming connections to the server
$connection = socket_accept($socket);
if($connection){
socket_write($connection, "You have connected to the socket...\n\r");
}
?>
你應該使用你的命令提示符來運行這個例子。理由是因為這里將產(chǎn)生一個服務器,而不是一個Web頁面。如果你嘗試使用Web瀏覽器來運行這個腳本,那么很有可能它會超過30秒的限時。你可以使用下面的代碼來設(shè)置一個無限的運行時間,但是還是建議使用命令提示符來運行。
set_time_limit(0);
在你的命令提示符中對這個腳本進行簡單測試:
Php.exe example01_server.php
如果你沒有在系統(tǒng)的環(huán)境變量中設(shè)置php解釋器的路徑,那么你將需要給php.exe指定詳細的路徑。當你運行這個服務器端的時候,你能夠通過遠程登陸(telnet)的方式連接到端口1337來測試這個服務器。
上面的服務器端有三個問題:1. 它不能接受多個連接。2. 它只完成唯一的一個命令。3. 你不能通過Web瀏覽器連接這個服務器。
這個第一個問題比較容易解決,你可以使用一個應用程序去每次都連接到服務器。但是后面的問題是你需要使用一個Web頁面去連接這個服務器,這個比較困難。你可以讓你的服務器接受連接,然后些數(shù)據(jù)到客戶端(如果它一定要寫的話),關(guān)閉連接并且等待下一個連接。
在上一個代碼的基礎(chǔ)上再改進,產(chǎn)生下面的代碼來做你的新服務器端:
<?php
// Set up our socket
$commonProtocol = getprotobyname("tcp");
$socket = socket_create(AF_INET, SOCK_STREAM, $commonProtocol);
socket_bind($socket, 'localhost', 1337); //socket_bind()???? 把socket綁定在一個IP地址和端口上
socket_listen($socket);
// Initialize the buffer
$buffer = "NO DATA";
while(true) {
// Accept any connections coming in on this socket
$connection = socket_accept($socket);//socket_accept()??? 接受一個Socket連接
printf("Socket connected\r\n");
// Check to see if there is anything in the buffer
if($buffer != ""){
printf("Something is in the buffer...sending data...\r\n");
socket_write($connection, $buffer . "\r\n"); //socket_write()???? 寫數(shù)據(jù)到socket緩存
printf("Wrote to socket\r\n");
}else {
printf("No Data in the buffer\r\n");
}
// Get the input
while($data = socket_read($connection, 1024, PHP_NORMAL_READ))//socket_read()???? 讀取指定長度的數(shù)據(jù)
{
$buffer = $data;
socket_write($connection, "Information Received\r\n");
printf("Buffer: " . $buffer . "\r\n");
}
socket_close($connection); //socket_close()???? 關(guān)閉一個socket資源
printf("Closed the socket\r\n\r\n");
}
?>
這 個服務器端要做什么呢?它初始化一個socket并且打開一個緩存收發(fā)數(shù)據(jù)。它等待連接,一旦產(chǎn)生一個連接,它將打印“Socket connected”在服務器端的屏幕上。這個服務器檢查緩沖區(qū),如果緩沖區(qū)里有數(shù)據(jù),它將把數(shù)據(jù)發(fā)送到連接過來的計算機。然后它發(fā)送這個數(shù)據(jù)的接受信 息,一旦它接受了信息,就把信息保存到數(shù)據(jù)里,并且讓連接的計算機知道這些信息,最后關(guān)閉連接。當連接關(guān)閉后,服務器又開始處理下一次連接。
◆ 產(chǎn)生一個客戶端
處理第二個問題是很容易的。你需要產(chǎn)生一個php頁連接一個socket,發(fā)送一些數(shù)據(jù)進它的緩存并處理它。然后你有個處理后的數(shù)據(jù)在還頓,你能夠發(fā)送你的數(shù)據(jù)到服務器。在另外一臺客戶端連接,它將處理那些數(shù)據(jù)。
下面的例子示范了使用socket:
<?php
// Create the socket and connect
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$connection = socket_connect($socket,’localhost’, 1337);
while($buffer = socket_read($socket, 1024, PHP_NORMAL_READ)) {
if($buffer == “NO DATA”) {
echo(“<p>NO DATA</p>”);
break;
}else{
// Do something with the data in the buffer
echo(“<p>Buffer Data: “ . $buffer . “</p>”);
}
}
echo(“<p>Writing to Socket</p>”);
// Write some test data to our socket
if(!socket_write($socket, “SOME DATA\r\n”)){
echo(“<p>Write failed</p>”);
}
// Read any response from the socket
while($buffer = socket_read($socket, 1024, PHP_NORMAL_READ)){
echo(“<p>Data sent was: SOME DATA<br> Response was:” . $buffer . “</p>”);
}
echo(“<p>Done Reading from Socket</p>”);
?>
這 個例子的代碼演示了客戶端連接到服務器。客戶端讀取數(shù)據(jù)。如果這是第一時間到達這個循環(huán)的首次連接,這個服務器將發(fā)送“NO DATA”返回給客戶端。如果情況發(fā)生了,這個客戶端在連接之上??蛻舳税l(fā)送它的數(shù)據(jù)到服務器,數(shù)據(jù)發(fā)送給服務器,客戶端等待響應。一旦接受到響應,那么 它將把響應寫到屏幕上。
資源分享:http://www.php100.com/html/webkaifa/PHP/PHP/2008/1218/969.html
轉(zhuǎn)載于:https://www.cnblogs.com/yuanzhong/p/4095732.html
總結(jié)
以上是生活随笔為你收集整理的php socket 基础知识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: loaded the ViewContr
- 下一篇: Myeclipse编译工程用Weblog