首頁  >  文章  >  後端開發  >  PHP 編寫基本的 Socket 程式

PHP 編寫基本的 Socket 程式

藏色散人
藏色散人轉載
2019-11-18 14:11:033570瀏覽

告誡年輕人

空想是沒有用的,個人的能力來自每一天的努力,而不是一步登天,不要畏懼任何新的知識,水滴石穿,總有一天會柳暗花明。

我的目的

因為在以後的學習中,我可能會用到網路方面的內容,但同時很多寫PHP 的coder 都沒寫過socket程序,但是肯定聽說過它,也肯定聽說過網絡編程這個詞,所以為了今後的學習,我打算在這裡先簡單的講解下相關知識,本篇博文自帶實例程序,代碼託管在碼雲(php-socket-base-code:https://gitee.com/obamajs/php-socket-base-code),你只需要下載下來,設定相關環境,按照指示即可運作。

環境配置

socket 程式設計需要開啟php 的socket 擴展,我使用的電腦是windows,所以這裡你只需要打開php.ini 文件,找到這一行去掉註解就可以了

extension=sockets

官方文件

php 的socket 程式設計的官方網址為:php socket(https://www.php.net/manual/ en/book.sockets.php)

服務端程式設計

socket 程式設計遵循一定的程式設計步驟,這幾個步驟缺一不可,客戶端和服務端程式設計有所區別,我們先來看看服務端。

PHP 編寫基本的 Socket 程式

建立套接字

套接字屬於系統資源,我們先呼叫socket_create 方法(參考官方文件:https: //www.php.net/manual/en/function.socket-create.php),呼叫如下:

$this->socket_handle = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$this->socket_handle) {
      //创建失败抛出异常,socket_last_error获取最后一次socket操作错误码,socket_strerror打印出对应错误码所对应的可读性描述
     throw new Exception(socket_strerror(socket_last_error($this->socket_handle)));
} else {
          echo "create socket successful\n";
}

第一個參數指定了,目前套接字是採用ipv4 還是ipv6,如果是前者的話,那麼傳遞AF_INET,否則AF_INET6,當然還有一種類型,就是AF_UNIX,這個暫時不討論,我們一般選擇AF_INET(ipv6 不是很普及)。

第二個參數,指定了協定的類型,一般選擇TCP 或是UDP,TCP 是可靠的串流(生活當中用的最為廣泛,保證了可靠性和安全性),UDP 則不是,這個參數一般選擇TCP。

第三個如果你之前選擇了 TCP,那麼它就是 SOL_TCP,否則就是 SOL_UDP。

綁定位址和連接埠號碼

因為一個主機可能存在多個ip 位址,所以你需要指定你的socket 監聽的是哪一個,常用的值為127.0.0.1,或是監聽所有位址0.0.0.0,那麼這裡可能有人不懂了,127.0.0.1 和0.0.0.0 有啥差別呢? 127.0.0.1 只是一個回環地址,只能用於本機訪問,說白了就是自己玩自己的,因為這個ip 不對外部開放,所以有人也就無法訪問這個地址,所以如果你的伺服器地址設定為127.0. 0.1,別人想要訪問,只能去屎吧。

0.0.0.0 嚴格來說不算是 ip 位址,它的意思是本機的所有 IP 位址,都是我的,哈哈。

明白了上面這個,我們來看這個呼叫的程式碼

if (!socket_bind($this->socket_handle, $this->addr, $this->port)) {
         throw new Exception(socket_strerror(socket_last_error($this->socket_handle)));
    } else {
         echo "bind addr successful\n";
 }

是不是很簡單,第一個參數就是socket_create 回傳的結果,第二個參數就是位址了,上面已經說過了,第三個參數是埠號。

監聽套接字

經過上面的這些步驟,我們只是創建了一個套接字並且給它綁定了連接埠號碼和位址,但是系統怎麼知道它是監聽套接字呢?所以呢,我們的事情還沒做完,所以我們得告訴它啊,別告訴我你和系統心有靈犀啊! ! !

if (!socket_listen($this->socket_handle, $this->back_log)) {
      throw new Exception(socket_strerror(socket_last_error($this->socket_handle)));
  } else {
      echo "socket  listen successful\n";
 }

第二個參數值得說明一哈,請聽我細細道來,對於linux 系統中的每一個進程而言,系統都維護著待處理套接字的隊列(先進先出,總得講個先來後到吧),上層程式處理業務邏輯總得需要時間吧,所以讓你你等你就等著嗆。那麼這個隊列的大小設定為多大呢?它的值就是這第二個參數,那我是不是可以設​​定的很大呢?騷年,你想太多了吧?不同的系統這個值有所不同,別說我忽悠你,看下面。

The maximum number passed to the backlog parameter highly depends on the underlying platform. On Linux, it is silently truncated to SOMAXCONN. On win32, if passed SOMAXCONN, the underlying service provider responsible for the socket will set the backlog to a maximum reasonable value. There is no standard provision to find out the actual backlog value on this platform.

你也不必關心這個值精確的數據,沒有什麼意義。

萬事俱備,只欠東風

經過上面的一通操作之後,我們可以開始接受來自客戶端的連接了,這個函數就更簡單了

$client_socket_handle = socket_accept($this->socket_handle);

這個函數的回傳值也是一個套接字句柄,所以你可以對它進行讀寫操作,在目前的實例程式中,我們做的事情很簡單,簡單到你可以懷疑人生了。

 $client_socket_handle = socket_accept($this->socket_handle);
        if (!$client_socket_handle) {
            echo "socket_accept call failed\n";
            exit(1);
        } else {
            while (true) {
                $bytes_num = socket_recv($client_socket_handle, $buffer, 100, 0);
                if (!$bytes_num) {
                    echo "socket_recv  failed\n";
                    exit(1);
                } else {
                    echo "content from client:" . $buffer . "\n";
                }
            }
        }

讀取套接字

以上面的範例為例,我們使用socket_recv 讀取來自客戶端的內容,這個函數很簡單,函數原型如下

socket_recv ( resource $socket , string &$buf , int $len , int $flags ) : int

讀取的內容會在第二個參數返回,第二個參數傳遞我們想要讀取的字元數,第四個參數可以直接設定為0,該函數的返回值為實際讀取的位元組數。

客戶端程式設計

客户端相对于服务端来说,就很简单了,流程如下

PHP 編寫基本的 Socket 程式

创建套接字前面已经讲过了,不再详述,客户端只需要连接服务器即可,函数为 socket_create,我们来看一哈在当前的例子中,我们是如何调用的。

if (!socket_connect($this->socket_handle, $this->server_addr, $this->server_port)) {
            echo socket_strerror(socket_last_error($this->socket_handle)) . "\n";
            exit(1);
        } else {
            while (true) {
                $data = fgets(STDIN);
                //如果用户输入quit,那么退出程序
                if (strcmp($data, "quit") == 0) {
                    break;
                }
                socket_write($this->socket_handle, $data);
            }
        }

该函数只需要指定服务器的地址和端口号即可,参数是不是很简单

练习实例

在讲解基本函数调用的时候,我就把自带程序的核心部分,复制出来了,如果要完整的程序,这里是地址(php-socket-base-code:https://gitee.com/obamajs/php-socket-base-code),代码非常简单,再次提醒,这些代码完全是用于给大家讲解基本的 socket 变成操作,为大家以后的学习打下基础,那么如何使用这个例子程序呢?

进入到命令行,开启服务器程序

php TcpServer.php,

打开另外一个命令行界面,

php TcpClient.php,

在客户端界面,输入任何文本,再输入回车,再切换到服务器界面,您将会看到客户端输入的内容

在笔者的电脑上操作实例截图如下:

PHP 編寫基本的 Socket 程式

以上是PHP 編寫基本的 Socket 程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:learnku.com。如有侵權,請聯絡admin@php.cn刪除