首頁  >  文章  >  後端開發  >  php中socket通訊詳解

php中socket通訊詳解

小云云
小云云原創
2018-03-16 11:34:011809瀏覽

有時候我們的php程式需要和其他系統通訊,例如一個企業的官網提供產品溯源資訊的查詢,在後台網站就需要和企業的溯源系統或ERP系統通訊,此時就需要進行php的網絡編程,php提供了一個sockets擴展,官網位址為:

http://nl3.php.net/manual/zh/intro.sockets.php

#該擴充功能讓我們有能力透過php直接操縱套接字socket,這樣就可以和其他系統通訊了,我們使用socket在OSI網路模型的傳輸層以上工作,直接使用TCP、 UDP提供的服務,因此可以使用它作為其他應用層協定的客戶端,例如模擬HTTP客戶端(瀏覽器),常見的smtp、pop、ftp都可以用它模擬,比較好玩的是可以使用它和允許TELNET的伺服器交互,這些協定都是應用層協定所以它都可以交互,自訂的系統間通訊就需要自訂協定了。

這裡提供一個範例,用php開發一個簡單的檔案接收伺服器,另一個php客戶端程式展示如何和這個伺服器程式通訊,程式碼如下。

伺服器端程式:

#
<?php
/**
 * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展
 * 这是一个简易的服务器程序,接收客户端发送的文件,保存后关闭
 * 通讯协议约定为:文件大小::文件扩展名::有效数据
 * 通讯结束标识符为:“-end-”
 * 在命令行或浏览器中执行均可,推荐命令行
 * 作者:云客【云游天下,作客四方】
 */

/****配置****/
//要绑定监听的本机ip地址和端口
$address = &#39;127.0.0.1&#39;;
$port = 81;

error_reporting(E_ALL);

//防止超时
set_time_limit(0);

//开启绝对刷送,禁止缓冲内容
ob_implicit_flush();

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
//绑定套接字到端口
if (socket_bind($sock, $address, $port) === false) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队
//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将直接忽略,客户端显示无法链接
//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队
if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
    //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源
    if (($client_sock = socket_accept($sock)) === false) {
        echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    echo "[client connect start]\n";
    $data = ""; //接收的数据
    $data_size = -1; //接收的数据大小
    $received_size = 0; //实际接收的数据
    $ext_name = "txt"; //默认文件扩展名

    //开始与客户端交互
    do {
        //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容
        if (false === ($buf = socket_read($client_sock, 2048))) {
            echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client_sock)) . "\n";
            break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器
        }
        if ($data_size === -1) {//第一次接受数据,解析通讯协议
            $arr = explode("::", $buf, 3);
            $data_size = (int)$arr[0];
            $ext_name = $arr[1];
            $data .= $arr[2];
            $received_size += strlen($data);
            continue;
        }
        $data .= $buf;
        $received_size += strlen($buf);
        if ($data_size <= $received_size) {
            if ($data_size < $received_size) {
                $data = substr($data, 0, $data_size);
            }

            echo "received:" . $received_size . "/" . "total:" . $data_size;
            $talkback = "-end-"; //发送通讯结束标志
            socket_write($client_sock, $talkback, strlen($talkback));
            file_put_contents("servertest." . $ext_name, $data);
            break 2; //关闭服务器
        }

        echo $talkback = "received:" . $received_size . "\n";
        socket_write($client_sock, $talkback, strlen($talkback));


    } while (true);
    socket_close($client_sock);

} while (true);

socket_close($sock);
?>

客戶端程式:

<?php
/**
 * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展
 * 这是一个简易的客户端程序,向服务器发送文件
 * 通讯协议约定为:文件大小::文件扩展名::有效数据
 * 通讯结束标识符为:“-end-”
 * 在命令行或浏览器中执行均可,推荐命令行,需先开启服务器端
 * 作者:云客【云游天下,作客四方】
 */

/****配置****/
$file = "yunke.jpg"; //待发送的文件
//服务器ip和端口号
$address = &#39;127.0.0.1&#39;;
$port = 81;


error_reporting(E_ALL);

// 防止超时 
set_time_limit(0);

// 开启绝对刷送,不要缓冲输出
ob_implicit_flush(true);

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    exit();
}
//打开到远程主机的链接
if (socket_connect($sock, $address, $port) === false) {
    echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    exit();
}

//构造一个很大的待发送数据 大约10MB
/*
$data = "";
for ($i = 1; $i <= 10000000; $i++) {
    $data .= "data:" . $i;
}
$data = $data . $data . $data;
*/

$data = file_get_contents($file);
$data_size = strlen($data);
$arr_temp = explode(&#39;.&#39;, $file);
$ext_name = end($arr_temp);
$sock_data = $data_size . "::" . $ext_name . "::" . $data;

//发送数据给远程主机
while (true) {
    $sock_data_size = strlen($sock_data);
    $send_size = socket_write($sock, $sock_data, $sock_data_size);
    if ($send_size === false) {
        echo "send false:" . socket_strerror(socket_last_error($sock)) . "\n";
        socket_close($sock);
        echo "[client shutdown]\n";
        exit();
    }
    if ($send_size == $sock_data_size) {
        break;
    }
    $sock_data = substr($sock_data, $send_size);
}

//读返回数据
while (true) {
    if (false === ($out = socket_read($sock, 2048))) {
        echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break 1;
    }

    echo "server: " . $out . "\n";
    if (substr($out, -5) == "-end-") { //约定结束标志
        break;
    }
}
//关闭套接字资源
socket_close($sock);
echo "[client shutdown]\nsend data:" . format($data_size);

function format($byte = 0)
{
    if ($byte > 1024 * 1024) {
        return ceil($byte / (1024 * 1024)) . "MB";
    } elseif ($byte > 1024) {
        return ceil($byte / 1024) . "KB";
    } else {
        return $byte . " Byte";
    }
}


該範例展示透過一個TCP短連結的方式向伺服器傳送一個文件,在實際的專案中通訊模組不會這麼簡單,需要考慮更多的問題

例如:

是否需要長連接的方式(一個tcp連結裡面多次來回傳送資料)、資料校驗防止損壞、不同系統間的大小端字節序問題、自訂通訊協定、黏包問題、逾時處理、並發存取、流量控制、TCP封包解包等等

如果想深入了解php網路程式設計以上提到的這些都需要係統學習,推薦看一看workerman的實作

它是一個php寫的socket伺服器框架,幫助解決socket通訊問題,使用它可以建立一個自定義伺服器等等
workerman官網位址為:http://www.workerman.net/

以下再提供兩個檢視伺服器HTTP頭和瀏覽器頭的範例程式:

以下範例查看伺服器傳回的頭信息,修改需要檢視的伺服器位址,然後在瀏覽器中存取該腳本即可:

<?php
error_reporting(E_ALL);

// 防止超时 
set_time_limit(0);

// 开启绝对刷送,不要缓冲输出
ob_implicit_flush(true);

$address = &#39;www.qq.com&#39;; //要查看的服务器
$port=80;

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
    exit();
}
//打开到远程主机的链接
if (socket_connect($sock, gethostbyname($address), $port) === false) {
    echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
    exit();
}

//构造http头
$msg = "GET / HTTP/1.1 \r\n";
$msg .= "Host: {$address}\r\n";
$msg .= "Connection: Close\r\n\r\n";  
//指示服务器发回完毕就断开,不必等等其他资源的链接
//如果没有该行会,服务器会一直等待我方继续发送数据,直到超时关闭
//而我方不会再发送,这个会让下面的socket_read函数互为等待,等很久

//发送给远程主机
socket_write($sock, $msg, strlen($msg));

$str="";
while ($out = @socket_read($sock, 2048*4)) {
    $str.=$out;
}
$str=explode("\r\n\r\n", $str);
$str=$str[0];
if($str)
{
    echo "<pre class="brush:php;toolbar:false">\r\n".$str."\r\n
"; }else{ echo "nothing"; } //关闭套接字资源 socket_close($sock);

以下是檢視瀏覽器頭的範例,可以方便的檢視瀏覽器所傳送的會話訊息
首先確定關閉了80個端口,然後修改本機的host文件,將想要存取的網址定向到本機127.0.0.1位址,使用php命令列模式啟動此腳本,然後使用瀏覽器存取已設定定向的網址即可,程式會一直開啟,如需關閉請在控制台使用ctrl+c組合鍵,程式如下:

<?php
error_reporting(E_ALL);

//防止超时
set_time_limit(0);

//开启绝对刷送,禁止缓冲内容
ob_implicit_flush();

$address = &#39;127.0.0.1&#39;;
$port = 80;

//创建套接字资源
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
//绑定套接字到端口
if (socket_bind($sock, $address, $port) === false) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
//开始监听端口,参数5表示可以让5个连接请求在缓冲中排队
//排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将显示无法链接
//注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队
if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
    //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源
    //可以使用socket_set_nonblock函数设置非阻塞模式
    if (($msgsock = socket_accept($sock)) === false) {
        echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    
    //链接成功后在控制台显示提示
    echo "[client start ".date("Y-m-d H:i:s")."]\n\n";
    
    do {
        //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容
        if (false === ($buf = socket_read($msgsock, 2048*6))) {
            echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
            break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器
        }
        $talkback ="HTTP/1.1 200 OK\r\n\r\n";
        $talkback .= date("Y-m-d H:i:s")."\nBrowser HTTP Headers:\n\n".$buf."\n";
        //向客户端显示该内容
        socket_write($msgsock, $talkback, strlen($talkback));
        //向服务器控制台显示该内容
        echo "$buf\n";
        break ; //中断本次与浏览器的链接
    } while (true);
    socket_close($msgsock);
} while (true);

socket_close($sock);
?>

相關推薦:

linux中關於socket通訊取得本地的來源連接埠號碼的實作方法

PHP之Socket伺服器建置與測試實例分享

#PHP中Socket簡單使用方法

#

以上是php中socket通訊詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn