PHP 網絡編程小白系列 —— Socket 編程入門

這篇文章將會介紹一下 Socket 編程中相關的 PHP 函數,並簡單實現一個 C/S 的交互

Socket 簡介

Socket 的官方解釋:
在網絡編程中最經常使用的方案即是Client/Server(客戶機/服務器)模型。在這種方案中客戶應用程序向服務器程序請求服務。一個服務程序一般在一個衆所周知的地址監聽對服務的請求,也就是說,服務進程一 直處於休眠狀態,直到一個客戶向這個服務的地址提出了鏈接請求。在這個時刻,服務程序被"驚醒"而且爲客戶提供服務-對客戶的請求做出適當的反應。爲了方便這種Client/Server模型的網絡編程,90年代初,由Microsoft聯合了其餘幾家公司共同制定了一套WINDOWS下的網絡編程接口,即WindowsSockets規範,它不是一種網絡協議,而是一套開放的、支持多種協議的Windows下的網絡編程接口。如今的Winsock已經基本上實現了與協議無關,你可使用Winsock來調用多種協議的功能,但較常使用的是TCP/IP協議。Socket實際在計算機中提供了一個通訊端口,能夠經過這個端口與任何一個具備Socket接口的計算機通訊。應用程序在網絡上傳輸,接收的信息都經過這個Socket接口來實現php

咱們能夠簡單的把 Socket 理解爲一個能夠連通網絡上不一樣計算機應用程序之間的管道,把一堆數據從管道的 A 端扔進去,則會從管道的 B 端(同時還能夠從C、D、E、F……端冒出來)。linux

注意:咱們會在不一樣語境下使用不一樣的詞語去修飾 socket,你只須要對它有個概念就行了,由於 socket 自己就沒有真正意義上的實體編程

Socket 函數介紹

Socket 通訊依次會進行 Socket 建立、 Socket 綁定、Socket 監聽、Socket 收發、Socket 關閉幾個階段,下面咱們列舉出 PHP 網絡編程中最經常使用也是必不可少的幾個經常使用的函數進行進一步的說明。服務器

socket_create

TODO : 建立一個新的 socket 資源
函數原型: resource socket_create ( int $domain , int $type , int $protocol )
它包含三個參數,分別以下:網絡

  • domain:AF_INET、AF_INET六、AF_UNIX,AF的釋義就 address family,地址族的意思,咱們經常使用的有 ipv四、ipv6
  • type: SOCK_STREAM、SOCK_DGRAM等,最經常使用的就是SOCK_STREAM,基於字節流的SOCKET類型,也是TCP協議使用的類型
  • protocol: SOL_TCP、SOL_UDP 這個就是具體使用的傳輸協議,通常可靠的傳輸咱們選擇 TCP,遊戲數據傳輸咱們通常選用 UDP 協議

socket_bind

TODO : 將建立的 socket 資源綁定到具體的 ip 地址和端口
函數原型: bool socket_bind ( resource $socket , string $address [, int $port = 0 ] )dom

它包含三個參數,分別以下:socket

  • socket: 使用socket_create建立的 socket 資源,能夠認爲是 socket 對應的 id
  • address: ip 地址
  • port: 監聽的端口號,WEB 服務器默認80端口

socket_listen

TODO : 在具體的地址下監聽 socket 資源的收發操做
函數原型: bool socket_listen ( resource $socket [, int $backlog = 0 ] )函數

它包含兩個個參數,分別以下:spa

  • socket: 使用socket_create建立的socket資源
  • backlog: 等待處理鏈接隊列的最大長度

socket_accept

TODO : 監聽以後,接收一個即未來臨的新的鏈接,若是鏈接創建成功,將返回一個新的 socket 句柄(你能夠理解爲子進程,一般父進程用來接收新的鏈接,子進程負責具體的通訊)
函數原型: resource socket_accept ( resource $socket )命令行

  • socket: 使用socket_create建立的socket資源

socket_write

TODO : 將指定的數據發送到 對應的 socket 管道
函數原型: int socket_write ( resource $socket , string $buffer [, int $length ] )

  • socket: 使用socket_create建立的socket資源
  • buffer: 寫入到socket資源中的數據
  • length: 控制寫入到socket資源中的buffer的長度,若是長度大於buffer的容量,則取buffer的容量

socket_read

TODO : 獲取傳送的數據
函數原型: int socket_read ( resource $socket , int $length )

  • socket: 使用socket_create建立的socket資源
  • length: socket資源中的buffer的長度

socket_close

TODO : 關閉 socket 資源
函數原型: void socket_close ( resource $socket )

  • socket: socket_accept或者socket_create產生的資源,不能用於stream資源的關閉

stream_socket_server

因爲建立一個SOCKET的流程老是 socket、bind、listen,因此PHP提供了一個很是方便的函數一次性建立、綁定端口、監聽端口

函數原型: resource stream_socket_server ( string $local_socket [, int &$errno [, string &$errstr [, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN [, resource $context ]]]] )

  • local_socket: 協議名://地址:端口號
  • errno: 錯誤碼
  • errstr: 錯誤信息
  • flags: 只使用該函數的部分功能
  • context: 使用stream_context_create函數建立的資源流上下文

socket 實現 C/S 交互

基於上面的函數咱們能夠很方便的去構建 socket 通訊程序(在這裏我但願讀者能單獨創建一個目錄好比socket 由於後續咱們還會創建不少文件)咱們先編輯一個服務端程序 server.php,以下:

<?php

date_default_timezone_set("Asia/Shanghai");
error_reporting(E_NOTICE );

/*  確保在鏈接客戶端時不會超時   */
set_time_limit(0);

$ip = '127.0.0.1';
$port = 8090;

/*
 +-------------------------------
 *    @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) {
    echo "socket_create() Why failure is:".socket_strerror($sock)."\n";
}

if(($ret = socket_bind($sock,$ip,$port)) < 0) {
    echo "socket_bind() Why failure is:".socket_strerror($ret)."\n";
}

if(($ret = socket_listen($sock,4)) < 0) {
    echo "socket_listen() Why failure is:".socket_strerror($ret)."\n";
}

echo "Start time:".date('Y-m-d H:i:s') . PHP_EOL;
echo "Listening at ".$ip.':'.$port.PHP_EOL;


do {
    /*  建立新的鏈接  */
    if (($msgsock = socket_accept($sock)) < 0) {
        echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
        break;
    } else {
        
    # 鏈接成功輸出 Socket id
    $i = (int)$msgsock;
    echo "welcome client $i";

        # 向客戶端通訊(反饋)
        $msg ="鏈接成功!\n";
        socket_write($msgsock, $msg, strlen($msg));
    }
    socket_close($msgsock);
} while (true);
socket_close($sock);
?>

再編輯一個客戶端程序client.php,以下:

<?php



set_time_limit(0);
$port = 8090;
$ip = "127.0.0.1";

/*
 +-------------------------------
 *    客戶端 socket 鏈接整個過程
 +-------------------------------
 *    @socket_create
 *    @socket_connect
 *    @socket_write
 *    @socket_read
 *    @socket_close
 +--------------------------------
 */


/**
 * @socket_connect:客戶端發起套接字鏈接
 * @param socket  resource $socket       建立的$socket資源
 * @param address string   SOCK_STREAM   IP地址|Unix套接字
 * @param port    int                    端口
 */

/**
 * @socket_create:建立並返回一個套接字
 * @param domain   string AF_INET         IPV4 網絡協議
 * @param type     string SOCK_STREAM     全雙工字節流(可用的套接字類型)
 * @param protocol string SOL_TCP         具體協議(IPV4下的TCP協議)
 * @param return   套接字
 */

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket < 0) {
    echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";
}else {
    echo "try to connect '$ip' port: '$port'...\n";
}


$result = socket_connect($socket, $ip, $port);  #socket_connect的返回值應該是boolean值
if ($result < 0) {
    echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
}else {
    # 鏈接成功輸出提示信息
    echo "connect successfully\n";

    # 向服務端發送數據
    socket_write($socket, " hello ", 1024);

    # 獲取服務端數據
    $result = socket_read($socket, 1024);
    echo "服務器回傳數據爲:" . $result;


    echo "CLOSE SOCKET...\n";
    socket_close($socket);
    echo "CLOSE OK\n";    
    
}



?>

而後咱們打開終端(命令行)進入文件目錄下依次執行:

php server.php
php client.php

運行效果以下:

圖片描述

注意服務器監聽時進程是掛起的不能進行其餘操做,你可能須要另起一個終端執行客戶端程序

Socket 編程入門結語

本篇文章就是爲你們整理了一下 PHP Socket 編程經常使用的函數並解釋了一下各自的意義,而後寫了一個簡單的 C/S 交互,但願你們對網絡編程有個比較直觀的認識,下篇文章我會簡單講講進程在網絡編程中的做用,這也是爲後面網絡模型的講解打個基礎

相關文章
相關標籤/搜索