【PHPsocket編程專題(實戰篇①)】php-socket通信演示


建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱為ClientSocket ,另一個運行於服務器端,稱為ServerSocket 。
套接字之間的連接過程分為三個步驟:服務器監聽,客戶端請求,連接確認。
服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。
客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求。
連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。

認識socket相關的PHP函數

socket_accept()  # 接受一個Socket連接
socket_bind()  # 把socket綁定在一個IP地址和端口上
socket_clear_error()  # 清除socket的錯誤或者最后的錯誤代碼
socket_close()  # 關閉一個socket資源
socket_connect()  # 開始一個socket連接
socket_create_listen()  # 在指定端口打開一個socket監聽
socket_create_pair()  # 產生一對沒有區別的socket到一個數組里
socket_create()  # 產生一個socket,相當於產生一個socket的數據結構
socket_get_option()  # 獲取socket選項
socket_getpeername()  # 獲取遠程類似主機的ip地址
socket_getsockname()  # 獲取本地socket的ip地址
socket_iovec_add()  # 添加一個新的向量到一個分散/聚合的數組
socket_iovec_alloc()  # 這個函數創建一個能夠發送接收讀寫的iovec數據結構
socket_iovec_delete()  # 刪除一個已經分配的iovec
socket_iovec_fetch()  # 返回指定的iovec資源的數據
socket_iovec_free()  # 釋放一個iovec資源
socket_iovec_set()  # 設置iovec的數據新值
socket_last_error()  # 獲取當前socket的最后錯誤代碼
socket_listen()  # 監聽由指定socket的所有連接
socket_read()  # 讀取指定長度的數據
socket_readv()  # 讀取從分散/聚合數組過來的數據
socket_recv()  # 從socket里結束數據到緩存
socket_recvfrom()  # 接受數據從指定的socket,如果沒有指定則默認當前socket
socket_recvmsg()  # 從iovec里接受消息
socket_select()  # 多路選擇
socket_send()  # 這個函數發送數據到已連接的socket
socket_sendmsg()  # 發送消息到socket
socket_sendto()  # 發送消息到指定地址的socket
socket_set_block()  # 在socket里設置為塊模式
socket_set_nonblock()  # socket里設置為非塊模式
socket_set_option()  # 設置socket選項
socket_shutdown()  # 這個函數允許你關閉讀、寫、或者指定的socket
socket_strerror()  # 返回指定錯誤號的詳細錯誤
socket_write()  # 寫數據到socket緩存
socket_writev()  # 寫數據到分散/聚合數組

創建一個socket

產生一個Socket,你需要三個變量:一個協議、一個socket類型和一個公共協議類型。產生一個socket有三種協議供選擇,繼續看下面的內容來獲取詳細的協議內容。
定義一個公共的協議類型是進行連接一個必不可少的元素。下面的表我們看看有那些公共的協議類型。

表一:協議

名字/常量 描述
AF_INET 這是大多數用來產生socket的協議,使用TCP或UDP來傳輸,用在IPv4的地址
AF_INET6 與上面類似,不過是來用在IPv6的地址
AF_UNIX 本地協議,使用在Unix和Linux系統上,它很少使用,一般都是當客戶端和服務器在同一台及其上的時候使用

表二:Socket類型

名字/常量 描述
SOCK_STREAM 這個協議是按照順序的、可靠的、數據完整的基於字節流的連接。這是一個使用最多的socket類型,這個socket是使用TCP來進行傳輸。
SOCK_DGRAM 這個協議是無連接的、固定長度的傳輸調用。該協議是不可靠的,使用UDP來進行它的連接。
SOCK_SEQPACKET 這個協議是雙線路的、可靠的連接,發送固定長度的數據包進行傳輸。必須把這個包完整的接受才能進行讀取。
SOCK_RAW 這個socket類型提供單一的網絡訪問,這個socket類型使用ICMP公共協議。(ping、traceroute使用該協議)
SOCK_RDM 這個類型是很少使用的,在大部分的操作系統上沒有實現,它是提供給數據鏈路層使用,不保證數據包的順序

表三:公共協議

名字/常量 描述
ICMP 互聯網控制消息協議,主要使用在網關和主機上,用來檢查網絡狀況和報告錯誤信息
UDP 用戶數據報文協議,它是一個無連接,不可靠的傳輸協議
TCP 傳輸控制協議,這是一個使用最多的可靠的公共協議,它能保證數據包能夠到達接受者那兒,如果在傳輸過程中發生錯誤,那么它將重新發送出錯數據包。

現在你知道了產生一個socket的三個元素,那么我們就在php中使用socket_create()函數來產生一個socket。這個socket_create()函數需要三個參數:一個協議、一個socket類型、一個公共協議。socket_create()函數運行成功返回一個包含socket的資源類型,如果沒有成功則返回false

socket通訊演示

05172951-a955fce4e5d04082828e717fe0e102f9.jpg-26kB

服務器端:server.php

<?php
//確保在連接客戶端時不會超時
set_time_limit(0);
$ip = '127.0.0.1';
$port = 1935;
/*
 +-------------------------------
 *    @socket通信整個過程
 +-------------------------------
 *    @socket_create
 *    @socket_bind
 *    @socket_listen
 *    @socket_accept
 *    @socket_read
 *    @socket_write
 *    @socket_close
 +--------------------------------
*/
/*----------------    以下操作都是手冊上的    -------------------*/

if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) { // 創建一個Socket鏈接
    echo "socket_create() 失敗的原因是:" . socket_strerror($sock) . "\n";
}

if (($ret = socket_bind($sock, $ip, $port)) < 0) { //綁定Socket到端口
    echo "socket_bind() 失敗的原因是:" . socket_strerror($ret) . "\n";
}
if (($ret = socket_listen($sock, 4)) < 0) { // 開始監聽鏈接鏈接
    echo "socket_listen() 失敗的原因是:" . socket_strerror($ret) . "\n";
}
$count = 0;
do {
    if (($msgsock = socket_accept($sock)) < 0) { //堵塞等待另一個Socket來處理通信
        echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
        break;
    } else {
        //發送消息到客戶端
        $msg = "測試成功!\n";
        socket_write($msgsock, $msg, strlen($msg)); 
        
        
        //接收客戶端消息
        echo "測試成功了啊\n";
        $buf = socket_read($msgsock, 8192); // 獲得客戶端的輸入
        $talkback = "收到的信息:$buf\n";
        echo $talkback;
        if (++$count >= 5) {
            break;
        };
    }
    //echo $buf;
    socket_close($msgsock);
} while (true);
socket_close($sock);
?>

然后php server.php,發現1935端口已經處於被監聽狀態;接下來我們只要運行客戶端程序即可連接上。

QQ截圖20151010134926.png-6.1kB

這樣就完成第一步服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。

接下來就第二步客戶端請求:

<?php
error_reporting(E_ALL);
set_time_limit(0);
echo "<h2>TCP/IP Connection</h2>\n";
$port = 1935;
$ip = "127.0.0.1";
/*
 +-------------------------------
 *    @socket連接整個過程
 +-------------------------------
 *    @socket_create
 *    @socket_connect
 *    @socket_write
 *    @socket_read
 *    @socket_close
 +--------------------------------
*/
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 第一個參數”AF_INET”用來指定域名;
// 第二個參數”SOCK_STREM”告訴函數將創建一個什么類型的Socket(在這個例子中是TCP類型),UDP是SOCK_DGRAM

if ($socket < 0) {
    echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";
} else {
    echo "OK.\n";
}
echo "試圖連接 '$ip' 端口 '$port'...\n";
$result = socket_connect($socket, $ip, $port);
if ($result < 0) {
    echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
} else {
    echo "連接OK\n";
}
$in = "Ho\r\n";
$in.= "first blood\r\n";
$out = '';
if (!socket_write($socket, $in, strlen($in))) {
    echo "socket_write() failed: reason: " . socket_strerror($socket) . "\n";
} else {
    echo "發送到服務器信息成功!\n";
    echo "發送的內容為:<font color='red'>$in</font> <br>";
}
while ($out = socket_read($socket, 8192)) {
    echo "接收服務器回傳信息成功!\n";
    echo "接受的內容為:", $out;
}
echo "關閉SOCKET...\n";
socket_close($socket);
echo "關閉OK\n";
?>

這時我們來看看各自的鏈接(先不管圖中的錯誤,這是我php配置有問題~)

QQ截圖20151010140519.png-29.4kB

QQ截圖20151010140341.png-10.5kB

然后服務器端接着處於監聽狀態,每次client請求都會接到反饋,注意該列使用的socket通訊方式其實是很落后的同步阻塞 IO 模型,其上還有同步非阻塞 IO 模型(select/poll 的同步模型)以及使用 epoll/kqueue 的異步模型:屬於異步阻塞/非阻塞 IO 模型;(大多數都是epoll/kqueue模型)

具體參考: http://www.cnblogs.com/lchb/articles/3078169.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM