摘要:概述這是關于學習的第五篇文章多協議多端口的應用。網絡通信協議設計多端口監聽的使用希望通過我提供的,能夠對文檔有更加深刻的理解。支持了種類型的自定義網絡通信協議結束符協議固定包頭包體協議。
概述
這是關于 Swoole 學習的第五篇文章:Swoole 多協議 多端口 的應用。
第四篇:Swoole HTTP 的應用
第三篇:Swoole WebSocket 的應用
第二篇:Swoole Task 的應用
第一篇:Swoole Timer 的應用
主要參考官方的這兩篇文章,進行實現的 Demo。
網絡通信協議設計:https://wiki.swoole.com/wiki/...
多端口監聽的使用:https://wiki.swoole.com/wiki/...
希望通過我提供的 Demo,能夠對文檔有更加深刻的理解。
網絡通信協議設計為什么需要通信協議?
官方:TCP協議在底層機制上解決了UDP協議的順序和丟包重傳問題。但相比UDP又帶來了新的問題,TCP協議是流式的,數據包沒有邊界。應用程序使用TCP通信就會面臨這些難題。因為TCP通信是流式的,在接收1個大數據包時,可能會被拆分成多個數據包發送。多次Send底層也可能會合并成一次進行發送。這里就需要2個操作來解決:分包 和 合包,所以TCP網絡通信時需要設定通信協議。
Swoole 支持了2種類型的自定義網絡通信協議 :EOF結束符協議、固定包頭+包體協議。
EOF結束符協議先看下,未設置協議的效果:
發送的每條數據長度都是 23,但在 onReceive 接收數據的時候每次接收的長度不一樣,并沒有按照想象的方式進行分包。
再看下,設置了EOF結束符協議的效果:
發送的每條數據長度都是 23,在 onReceive 接收數據的時候每次接收的也是 23 ,完美。
主要設置項如下:
"package_max_length" => "8192", "open_eof_split" => true, "package_eof" => " "
不做解釋,官方文檔已經寫的很清楚。
示例代碼如下:
server.php
serv = new swoole_server("0.0.0.0", 9501); $this->serv->set([ "worker_num" => 2, //開啟2個worker進程 "max_request" => 4, //每個worker進程 max_request設置為4次 "dispatch_mode" => 2, //數據包分發策略 - 固定模式 //EOF結束符協議 "package_max_length" => "8192", "open_eof_split" => true, "package_eof" => " " ]); $this->serv->on("Start", [$this, "onStart"]); $this->serv->on("Connect", [$this, "onConnect"]); $this->serv->on("Receive", [$this, "onReceive"]); $this->serv->on("Close", [$this, "onClose"]); $this->serv->start(); } public function onStart($serv) { echo "#### onStart ####".PHP_EOL; echo "SWOOLE ".SWOOLE_VERSION . " 服務已啟動".PHP_EOL; echo "swoole_cpu_num:".swoole_cpu_num().PHP_EOL; echo "master_pid: {$serv->master_pid}".PHP_EOL; echo "manager_pid: {$serv->manager_pid}".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onConnect($serv, $fd) { echo "#### onConnect ####".PHP_EOL; echo "客戶端:".$fd." 已連接".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onReceive($serv, $fd, $from_id, $data) { echo "#### onReceive ####".PHP_EOL; var_dump($data); } public function onClose($serv, $fd) { echo "Client Close.".PHP_EOL; } } $server = new Server();
client.php
client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $this->client->on("Connect", [$this, "onConnect"]); $this->client->on("Close", [$this, "onClose"]); $this->client->on("Error", [$this, "onError"]); } public function connect() { if(!$fp = $this->client->connect("127.0.0.1", 9501)) { echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL; return; } } public function onConnect() { fwrite(STDOUT, "發送測試數據(Y or N):"); swoole_event_add(STDIN, function() { $msg = trim(fgets(STDIN)); if ($msg == "y") { $this->send(); } fwrite(STDOUT, "發送測試數據(Y or N):"); }); } public function send() { $msg_info = "客戶端發信息... "; $i = 0; while ($i < 50) { var_dump($msg_info); $this->client->send($msg_info); $i++; } } public function onClose() { echo "Client close connection".PHP_EOL; } public function onError() { } } $client = new Client(); $client->connect();固定包頭+包體協議
先看下,未設置協議的效果:
很明顯,在 onReceive 接收到的數據,是少的。
再看下,設置協議的效果:
主要設置項如下:
"open_length_check" => true, "package_max_length" => "8192", "package_length_type" => "N", "package_length_offset" => "0", "package_body_offset" => "4",
不做解釋,官方文檔已經寫的很清楚。
示例代碼如下:
server.php
serv = new swoole_server("0.0.0.0", 9501); $this->serv->set([ "worker_num" => 2, //開啟2個worker進程 "max_request" => 4, //每個worker進程 max_request設置為4次 "dispatch_mode" => 2, //數據包分發策略 - 固定模式 //固定包頭+包體協議 "open_length_check" => true, "package_max_length" => "8192", "package_length_type" => "N", "package_length_offset" => "0", "package_body_offset" => "4", ]); $this->serv->on("Start", [$this, "onStart"]); $this->serv->on("Connect", [$this, "onConnect"]); $this->serv->on("Receive", [$this, "onReceive"]); $this->serv->on("Close", [$this, "onClose"]); $this->serv->start(); } public function onStart($serv) { echo "#### onStart ####".PHP_EOL; echo "swoole_cpu_num:".swoole_cpu_num().PHP_EOL; echo "SWOOLE ".SWOOLE_VERSION . " 服務已啟動".PHP_EOL; echo "master_pid: {$serv->master_pid}".PHP_EOL; echo "manager_pid: {$serv->manager_pid}".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onConnect($serv, $fd) { echo "#### onConnect ####".PHP_EOL; echo "客戶端:".$fd." 已連接".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onReceive($serv, $fd, $from_id, $data) { echo "#### onReceive ####".PHP_EOL; $length = unpack("N", $data)[1]; echo "Length:".$length.PHP_EOL; $msg = substr($data, -$length); echo "Msg:".$msg.PHP_EOL; } public function onClose($serv, $fd) { echo "Client Close.".PHP_EOL; } } $server = new Server();
client.php
client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $this->client->on("Connect", [$this, "onConnect"]); $this->client->on("Close", [$this, "onClose"]); $this->client->on("Error", [$this, "onError"]); } public function connect() { if(!$fp = $this->client->connect("127.0.0.1", 9501, 1)) { echo "Error: {$fp->errMsg}[{$fp->errCode}]".PHP_EOL; return; } } public function onConnect() { fwrite(STDOUT, "發送測試數據(Y or N):"); swoole_event_add(STDIN, function() { $msg = trim(fgets(STDIN)); if ($msg == "y") { $this->send(); } fwrite(STDOUT, "發送測試數據(Y or N):"); }); } public function send() { $msg = "客戶端發的信息..."; $msg_info = pack("N", strlen($msg)).$msg; $i = 0; while ($i < 50) { var_dump($msg_info); $this->client->send($msg_info); $i++; } } public function onClose() { echo "Client close connection".PHP_EOL; } public function onError() { } } $client = new Client(); $client->connect();多端口監聽的使用
上圖,是示例代碼中的端口監聽:
9501 onMessage 處理 WebSocket。
9501 onRequest 處理 HTTP。
9502 onReceive 處理 TCP。
9503 onPacket 處理 UDP。
不多說,看下效果圖:
示例代碼如下:
server.php
serv = new swoole_websocket_server("0.0.0.0", 9501); $this->serv->set([ "worker_num" => 2, //開啟2個worker進程 "max_request" => 4, //每個worker進程 max_request設置為4次 "task_worker_num" => 4, //開啟4個task進程 "dispatch_mode" => 4, //數據包分發策略 - IP分配 "daemonize" => false, //守護進程(true/false) ]); $this->serv->on("Start", [$this, "onStart"]); $this->serv->on("Open", [$this, "onOpen"]); $this->serv->on("Message", [$this, "onMessage"]); $this->serv->on("Request", [$this, "onRequest"]); $this->serv->on("Close", [$this, "onClose"]); $this->serv->on("Task", [$this, "onTask"]); $this->serv->on("Finish", [$this, "onFinish"]); //監聽 9502 端口 $tcp = $this->serv->listen("0.0.0.0", 9502, SWOOLE_SOCK_TCP); $tcp->set([ "worker_num" => 2, //開啟2個worker進程 "max_request" => 4, //每個worker進程 max_request設置為4次 "dispatch_mode" => 2, //數據包分發策略 - 固定模式 //固定包頭+包體協議 "open_length_check" => true, "package_max_length" => "8192", "package_length_type" => "N", "package_length_offset" => "0", "package_body_offset" => "4", ]); $tcp->on("Receive", [$this, "onReceive"]); //監聽 9503 端口 $udp = $this->serv->listen("0.0.0.0", 9503, SWOOLE_SOCK_UDP); $udp->set([ "worker_num" => 2, //開啟2個worker進程 "max_request" => 4, //每個worker進程 max_request設置為4次 "dispatch_mode" => 2, //數據包分發策略 - 固定模式 ]); $udp->on("Packet", [$this, "onPacket"]); $this->serv->start(); } public function onStart($serv) { echo "#### onStart ####".PHP_EOL; echo "SWOOLE ".SWOOLE_VERSION . " 服務已啟動".PHP_EOL; echo "master_pid: {$serv->master_pid}".PHP_EOL; echo "manager_pid: {$serv->manager_pid}".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onOpen($serv, $request) { echo "#### onOpen ####".PHP_EOL; echo "server: handshake success with fd{$request->fd}".PHP_EOL; $serv->task([ "type" => "login" ]); echo "########".PHP_EOL.PHP_EOL; } public function onTask($serv, $task_id, $from_id, $data) { echo "#### onTask ####".PHP_EOL; echo "#{$serv->worker_id} onTask: [PID={$serv->worker_pid}]: task_id={$task_id}".PHP_EOL; $msg = ""; switch ($data["type"]) { case "login": $msg = "我來了..."; break; case "speak": $msg = $data["msg"]; break; } foreach ($serv->connections as $fd) { $connectionInfo = $serv->connection_info($fd); if (isset($connectionInfo["websocket_status"]) && $connectionInfo["websocket_status"] == 3) { $serv->push($fd, $msg); //長度最大不得超過2M } } $serv->finish($data); echo "########".PHP_EOL.PHP_EOL; } public function onFinish($serv,$task_id, $data) { echo "#### onFinish ####".PHP_EOL; echo "Task {$task_id} 已完成".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onClose($serv, $fd) { echo "#### onClose ####".PHP_EOL; echo "client {$fd} closed".PHP_EOL; echo "########".PHP_EOL.PHP_EOL; } public function onMessage($serv, $frame) { echo "#### onMessage ####".PHP_EOL; echo "receive from fd{$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}".PHP_EOL; $serv->task(["type" => "speak", "msg" => $frame->data]); echo "########".PHP_EOL.PHP_EOL; } public function onRequest($request, $response) { echo "#### onRequest ####".PHP_EOL; $response->header("Content-Type", "text/html; charset=utf-8"); $server = $request->server; $path_info = $server["path_info"]; $request_uri = $server["request_uri"]; echo "PATH_INFO:".$path_info.PHP_EOL; if ($path_info == "/favicon.ico" || $request_uri == "/favicon.ico") { return $response->end(); } $html = "你好 Swoole.
"; $response->end($html); } public function onReceive($serv, $fd, $from_id, $data) { echo "#### onReceive ####".PHP_EOL; $length = unpack("N", $data)[1]; echo "Length:".$length.PHP_EOL; $msg = substr($data, -$length); echo "Msg:".$msg.PHP_EOL; } public function onPacket($serv, $data, $clientInfo) { echo "#### onPacket ####".PHP_EOL; $serv->sendto($clientInfo["address"], $clientInfo["port"], "Server ".$data); var_dump($clientInfo); } } $server = new Server();
4 個客戶端連接的代碼分別是:
1、9501 onMessage 處理 WebSocket。可以參考原來文章 Swoole WebSocket 的應用 中的代碼即可。
2、9501 onRequest 處理 HTTP。可以參考原來文章 Swoole HTTP 的應用 中的代碼即可。
3、9502 onReceive 處理 TCP。可以參考原來文章 Swoole Task 的應用 中的代碼即可。
4、9503 onPacket 處理 UDP。
示例代碼:
netcat -u 10.211.55.4 9503小結
一、多端口的應用場景是什么?
比如,開發一個直播網站,直播用一個端口,IM聊天用一個端口。
比如,開發一個RPC服務,數據通訊用一個端口,統計界面用一個端口。
推薦閱讀系統的講解 - SSO 單點登錄
系統的講解 - PHP WEB 安全防御
系統的講解 - PHP 緩存技術
系統的講解 - PHP 接口簽名驗證
系統的講解 - PHP 浮點數高精度運算
本文歡迎轉發,轉發請注明作者和出處,謝謝!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/31480.html
摘要:是一個基于擴展實現的輕量級高性能的常駐內存型的和應用服務框架高度封裝了,,服務器,以及基于實現可擴展的服務,同時支持包方式安裝部署項目。基于實用,抽象事件處理類,實現與底層的回調的解耦,支持同步異步調用,內置等常用組件等。 swoolefy swoolefy是一個基于swoole擴展實現的輕量級高性能的常駐內存型的API和Web應用服務框架,高度封裝了http,websocket,ud...
摘要:的部分是基于以及協議的。例如父進程向中寫入子進程從中讀取子進程向中寫入父進程從中讀取。默認使用對進程進行分配交給對應的線程進行監聽線程收到某個進程的數據后會進行處理值得注意的是這個線程可能并不是發送請求的那個線程。 作者:施洪寶 一. 基礎知識 1.1 swoole swoole是面向生產環境的php異步網絡通信引擎, php開發人員可以利用swoole開發出高性能的server服務。...
摘要:概述這是關于入門學習的第八篇文章連接池的實現。開始今天的文章,這篇文章實現了連接池,代碼是在的實現文章的基礎上進行開發的。 概述 這是關于 Swoole 入門學習的第八篇文章:Swoole MySQL 連接池的實現。 第七篇:Swoole RPC 的實現 第六篇:Swoole 整合成一個小框架 第五篇:Swoole 多協議 多端口 的應用 第四篇:Swoole HTTP 的應用 第三...
摘要:概述這是關于學習的第七篇文章的實現。還有一些大佬加了微信,可能是出于對晚輩的提攜吧,偷偷告訴你,從大佬的朋友圈能學到很多東西。就到這了,上面的需要源碼的,加我微信。 概述 這是關于 Swoole 學習的第七篇文章:Swoole RPC 的實現。 第六篇:Swoole 整合成一個小框架 第五篇:Swoole 多協議 多端口 的應用 第四篇:Swoole HTTP 的應用 第三篇:Swo...
摘要:概述這是關于入門學習的第十篇文章壓測性能。測試機上安裝的虛擬機系統內存數量核數數量大小代碼壓測腳本并發量請求量壓測結果第次第次第次以上是壓測結果,供參考。小結通過這個壓測結果,表明的執行效率是杠杠的當然還有一些參數是可以調優的,比如等。 概述 這是關于 Swoole 入門學習的第十篇文章:壓測 swoole_websocket_server 性能。 第九篇:Swoole Redis ...
閱讀 3250·2023-04-25 22:47
閱讀 3765·2021-10-11 10:59
閱讀 2300·2021-09-07 10:12
閱讀 4243·2021-08-11 11:15
閱讀 3432·2019-08-30 13:15
閱讀 1750·2019-08-30 13:00
閱讀 968·2019-08-29 14:02
閱讀 1681·2019-08-26 13:57