首頁 > 後端開發 > php教程 > PHP實作系統程式設計之本地套接字(Unix Domain Socket)

PHP實作系統程式設計之本地套接字(Unix Domain Socket)

不言
發布: 2023-03-23 16:54:01
原創
3023 人瀏覽過

本篇文章給大家分享的內容是關於PHP實作系統程式設計之本地套接字(Unix Domain Socket),有著一定的參考價值,有需要的朋友可以參考一下

Socket API一開始是為了解決網路通訊而設計的,而後來在此之上又衍生出一種叫做本地套接字(Unix Domain Socket)的技術,本地套接字顧名思義,只支援本地的兩個進程之間進行通信,雖然網路套接字(Internet Domain Socket)也可以透過本地回環位址(127.0.0.1)來實現本地進程間通信,但由於本地套接字不需要經過網路協定棧,封包拆包、計算校驗和等操作,所以效率上比起網路套接字有一定的優點。由於本地套接字效能高、穩定、支援非血緣關係的進程間通訊,所以本地套接字也是當下使用最廣泛的IPC(進程間通訊)的機制之一。

Nginx 與PHP-FPM 之間使用網路套接字(127.0.0.1:9000)和使用本地套接字兩種通訊方式的效能對比

一般我們都是讓PHP-FPM 監聽127.0.0.1:9000 ,顯然這時Nginx 與PHP-FPM 是透過網路套接字來實現通訊的,其實,如果Nginx和PHP-FPM運作在同一台伺服器上,我們也可以讓PHP-FPM監聽本地套接字,接下來就針對這兩種方式的效能做一簡單的比較。

這裡我的Nginx開啟兩個worker程序

#
[root@localhost ~]# ps -ef | grep nginx
root      1838     1  0 22:48 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx     1839  1838  0 22:48 ?        00:00:00 nginx: worker process                   
nginx     1840  1838  0 22:48 ?        00:00:00 nginx: worker process                   
root      1851  1797  0 22:49 pts/0    00:00:00 grep nginx
登入後複製

 

使用網路套接字,Nginx與PHP-FPM配置分別如下:


 



##壓力測試test.php腳本


#

<?php

phpinfo();
登入後複製

壓測結果:







#再來看看Nginx和PHP-FPM 用本地套接字方式的通信,Nginx 和PHP-FPM 的配置稍作修改:





壓測結果:

以上測試都是多次壓測後取得結果,從結果可以看到,本地套接字要比網路套接字的QPS平均高了100多,和我們預期大致一致。



#PHP的本機套接字程式設計

其實PHP的本地套接字程式設計和網路套接字基本上一致,只是傳的參數不一樣。


PHP為socket程式設計提供了兩套API,一套是socket_* 系列方法,這在我們前面的系列文章裡演示過了,另一套是stream_socket_ * 系列方法,而後者使用起來更加的方便,這裡我們採用後者來示範。


stream_socket_*  
方法清單:






#########################
•stream_socket_accept — 接受由 stream_socket_server 创建的套接字连接
•stream_socket_client — Open Internet or Unix domain socket connection
•stream_socket_enable_crypto — Turns encryption on/off on an already connected socket
•stream_socket_get_name — 获取本地或者远程的套接字名称
•stream_socket_pair — 创建一对完全一样的网络套接字连接流
•stream_socket_recvfrom — Receives data from a socket, connected or not
•stream_socket_sendto — Sends a message to a socket, whether it is connected or not
•stream_socket_server — Create an Internet or Unix domain server socket
•stream_socket_shutdown — Shutdown a full-duplex connection
登入後複製
#########具體方法的使用請參閱PHP手冊,這裡直接示範程式碼:#######################server端程式碼:##################
<?php
//stream_server.php

$sockfile = &#39;/dev/shm/unix.sock&#39;;
// 如果sock文件已存在,先尝试删除
if (file_exists($sockfile))
{
    unlink($sockfile);
}

$server = stream_socket_server("unix://$sockfile", $errno, $errstr);

if (!$server)
{
        die("创建unix domain socket fail: $errno - $errstr");
}

while(1)
{
        $conn = stream_socket_accept($server, 5);

        if ($conn)
        {
                while(1)
                {
                    $msg = fread($conn, 1024);
                    if (strlen($msg) == 0) //客户端关闭
                    {
                        fclose($conn);
                        break;
                    }
                    echo "read data: $msg";
                    fwrite($conn, "read ok!");
                }
        }

}
fclose($server);
登入後複製
############################################################### ####client端程式碼:#########################
<?php
//stream_client.php

$client = stream_socket_client("unix:///dev/shm/unix.sock", $errno, $errstr);

if (!$client)
{
        die("connect to server fail: $errno - $errstr");
}

while(1)
{
    $msg = fread(STDIN, 1024);

    if ($msg == "quit\n")
    {
        break;
    }

    fwrite($client, $msg);
    $rt = fread($client, 1024);

    echo $rt . "\n";
}

fclose($client);
登入後複製
################# #################

运行

server端:


[root@localhost html]# php stream_server.php 
read data: hello unix domain socket
read data: are you ok?
read data: I&#39;m fine!
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13
登入後複製



client端:


[root@localhost html]# php stream_client.php 
hello unix domain socket
read ok!
are you ok?
read ok!
I&#39;m fine! 
read ok!
^C
登入後複製


以上是一个最简单的本地套接字的代码演示,细心的读者可能注意到了server端报的warning,服务器如果长时间没有客户端过来连接,超过了stream_socket_accept 方法设置的timeout,服务器端便会报这个警告,事实上,真正的服务端代码是不会是像这样写的,因为这种方式同一时间只能处理一个客户端连接,如果要实现并发,一种方式就是使用IO多路复用,如同 socket_* 系列方法中有socket_select 方法 (参考系列文章第一篇http://blog.csdn.net/zhang197093/article/details/77366407),stream_socket_* 系列方法提供了 stream_select 方法来实现多路复用,使用方法也很相似。

int stream_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] )
登入後複製


The stream_select() function accepts arrays of streams and waits for them to change status. Its operation is equivalent to that of the socket_select() function except in that it acts on streams.


详细的方法介绍请参考PHP手册 : http://php.net/manual/zh/function.stream-select.php

优化后的代码如下:


<?php
//stream_server.php

$sockfile = &#39;/dev/shm/unix.sock&#39;;
// 如果sock文件已存在,先尝试删除
if (file_exists($sockfile))
{
    unlink($sockfile);
}

$server = stream_socket_server("unix://$sockfile", $errno, $errstr);


if (!$server)
{
        die("创建unix domain socket fail: $errno - $errstr");
}

$listen_reads = array($server);
$listen_writes = array();
$listen_excepts = NULL;

while(1)
{
        $can_reads = $listen_reads;
        $can_writes = $listen_writes;
        $num_streams = stream_select($can_reads, $can_writes, $listen_excepts, 0);

        if ($num_streams)
        {
                foreach ($can_reads as &$sock)
                {
                        if ($server == $sock)
                        {
                                $conn = stream_socket_accept($server, 5); //此时一定存在客户端连接,不会有超时的情况
                                if ($conn)
                                {
                                        // 把客户端连接加入监听
                                        $listen_reads[] = $conn;
                                        $listen_writes[] = $conn;
                                }
                        }
                        else
                        {
                                $msg = fread($sock, 1024);  //此时一定是可读的
                                if (strlen($msg) == 0) //读取到0个字符,说明客户端关闭
                                {
                                        fclose($sock);
                                        // 从sock监听中移除
                                        $key = array_search($sock, $listen_reads);
                                        unset($listen_reads[$key]);
                                        $key = array_search($sock, $listen_writes);
                                        unset($listen_writes[$key]);
                                        echo "客户端关闭\n";
                                }
                                else
                                {
                                   echo "read data: $msg";
                                    // 是否可写
                                    if (in_array($sock, $can_writes))
                                    {
                                        fwrite($conn, "read ok!");
                                    }
                                }
                        }
                }
        }


}
fclose($server);
登入後複製


此时这个server就不会有前面那个Warning了,并且支持并发


[root@localhost html]# php stream_server.php 
read data: hello world
read data: hello unix domain socket
read data: harry up
read data: 
read data: 
read data: I&#39;m another client
客户端关闭
客户端关闭
read data: I&#39;m the third client
客户端关闭
登入後複製


That‘s all!
相关推荐:

PHP实现系统编程之 多进程编程介绍及孤儿进程、僵尸进程

PHP实现系统编程之网络Socket及IO多路复用


以上是PHP實作系統程式設計之本地套接字(Unix Domain Socket)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板