與常規web開發不一樣,使用socket開發能夠擺脫http的限制。可自定義協議,使用長鏈接、PHP代碼常駐內存等。學習資料來源於workerman官方視頻與文檔.php
一般建立一個socket服務包括這幾個簡單的步驟:web
1.建立一個socket套接字,監聽在某協議的某個端口,如:tcp的9865端口,爲了是外網能夠訪問,地址爲0.0.0.0,監聽地址應爲這種格式tcp://0.0.0.0:9865瀏覽器
2.將監聽socket設置爲非阻塞,若不設置,程序會在客戶端鏈接沒有發消息時阻塞。socket
3.程序阻塞在I/0複用函數stream_select,一旦有讀取到的新事件則進行處理。tcp
4.處理客戶端發送的數據。若讀取的socket鏈接爲監聽socket表示有新的鏈接,不然爲當前鏈接發送了數據。若讀取到空或返回了false,則表示客戶端斷開。函數
一個簡單的demo:學習
<?php class Worker{ //監聽socket protected $socket = NULL; //全部的socket鏈接 protected $allSockets = array(); //鏈接事件回調 public $onConnect = NULL; //斷線事件回調 public $onClose = NULL; //接收消息事件回調 public $onMessage = NULL; public function __construct($socket_address) { //建立一個socket監聽 $this->socket = stream_socket_server($socket_address); //設置爲非阻塞 stream_set_blocking($this->socket, 0); //將socket監聽加入allSockets $this->allSockets[(int)$this->socket] = $this->socket; } public function run() { while(true) { //不監聽可寫事件與帶外數據事件 $write = $except = array(); //監聽全部的socket事件 $read = $this->allSockets; //整個進程阻塞在這裏,持續監聽可讀事件 //此處參數均爲引用傳遞,在函數中會改變傳值 stream_select($read, $write, $except, 60); //處理全部可讀事件 foreach ($read as $index => $socket) { //若是是監聽socket,此處表示有新的鏈接 if ($socket === $this->socket) { //經過stream_socket_accept獲取新的鏈接 $new_conn_socket = stream_socket_accept($socket); if ($this->onConnect) { //觸發鏈接事件的回調,並將當前鏈接傳遞給回掉函數 call_user_func($this->onConnect, $socket); } //記錄此socket鏈接,以便於sream_select監聽可讀事件 $this->allSockets[(int)$new_conn_socket] = $new_conn_socket; } else //若是可讀事件不爲監聽socket,則表示對應客戶端有數據發過來 { //從鏈接中讀取數據 $buffer = fread($socket, 65535); //若是數據爲空,表示客戶端已經斷開鏈接 if ('' === $buffer || false === $buffer) { //嘗試觸發onClose回調 if ($this->onClose) { call_user_func($this->onClose, $socket); } fclose($socket); //關閉socket鏈接並從allSockets中刪除 unset($this->allSockets[(int)$socket]); continue; } //表示一個正常的鏈接,已經讀取到消息,交給回掉函數處理 if ($this->onMessage) { call_user_func($this->onMessage, $socket, $buffer); } } } } } } $worker = new Worker('tcp://0.0.0.0:9865'); $worker->onConnect = function ($conn) { echo '新的鏈接來了'; }; $worker->onClose = function ($conn) { echo '鏈接斷開了'; }; $worker->onMessage = function ($conn, $message) { $http_resonse = "HTTP/1.1 200 OK\r\n"; $http_resonse .= "Connection: keep-alive\r\n"; $http_resonse .= "Server: php socket server\r\n"; $http_resonse .= "Content-length: 11\r\n\r\n"; $http_resonse .= "hello world"; fwrite($conn, $http_resonse); }; $worker->run();
在cli環境下運行腳本:$ php worker.php ,this
而後使用瀏覽器訪問本地的9865端口便可看到咱們的hello world.net