WebSocket
WebSocket protocol was born in 2008 and became an international standard in 2011. All browsers are already supported.
Its greatest feature is that the server can actively push information to the client, and the client can also actively send information to the server. It is a real two-way equal dialogue and belongs to a kind of server push technology.
Why do you need WebSocket?
Requirements are: users stay on the page for 15 minutes without any operation, then pop up the landing window, so that users can re-login.
Generally, the implementation of this requirement is long connection polling, which will lead to the problems of browser's carton, server consumption and difficult maintenance.
Later, it was found that such a way of communication as websocket has the following advantages:
- Based on TCP protocol, the implementation of server side is relatively easy.
- Hand shielding is not easy, and it can pass through various HTTP proxy servers.
- Data format is lightweight, performance overhead is small, and communication efficiency is high.
- You can send either text or binary data.
- Without homology restriction, the client can communicate with any server.
- The protocol identifier is ws (wss if encrypted), and the server address is the URL.
The goal has been chosen, so how to achieve it?
PHP has a very useful asynchronous network communication framework, swoole, which saves its own time to implement websocket services. I used the laravel framework, and ultimately chose laravel-swoole Expansion.
Installation Configuration
Introduce laravel-swoole extension package wiki . With websocket.enabled and other configuration enabled, it is very convenient to manage services by naming them as follows:
php artisan swoole:http {start|stop|restart|reload|infos}
Modify the default handler configuration in the configuration file to customize the class: mainly to customize some callbacks in the lifecycle of the websocket.
/* |-------------------------------------------------------------------------- | Websocket handler for onOpen and onClose callback | Replace this handler if you want to customize your websocket handler |-------------------------------------------------------------------------- */ 'handler' => \App\Listeners\Swoole\WebsocketHandler::class, /* |-------------------------------------------------------------------------- | Default frame parser | Replace it if you want to customize your websocket payload |-------------------------------------------------------------------------- */ 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class,
When the configuration is complete, a file named websocket.php is added to the routes directory. It's very convenient to define events just like laravel routing. For example:
//Websocket::on('open', function ($websocket, Request $request) { // Log::info('websocket','open 111 +' . $websocket->getSender()); //}); // //Websocket::on('connect', function ($websocket, Request $request) { // Log::info('websocket','Connected ++ 222' . $websocket->getSender()); // // called while socket on connect. //}); // //Websocket::on('disconnect', function ($websocket) { // Log::info('websocket','Disconnected ++ 333' . $websocket->getSender()); // // called while socket on disconnect //}); // The checkLogin method in UserController takes two parameters, $websocket, $data. Websocket::on('loginCheck', "App\Http\Controllers\Api\UserController@checkLogin"); Websocket::on('logout', "App\Http\Controllers\Api\UserController@sendLogout");
Use
Controller:
public function checkLogin($websocket, $data) { if (empty($data['holding'])) { $websocket->emit('message', ['code' => self::HTTP_UNPROCESSABLE_ENTITY, 'message' => "Parameter error"]); return false; } $flag = true; $step = 1; while ($flag) { $step++; if ( ! $this->validateLoginStatus($data['holding'])) { $websocket->emit('message', ['code' => self::HTTP_UNAUTHORIZED, 'message' => "Landing timeout"]); unset($step); $flag = false; }else { if($step === 1) { $websocket->emit('message', ['code' => self::HTTP_OK, 'message' => "success"]); } } sleep(1); } }
Front-end call
Here we must pay attention to the structure of the data package. We have trampled many holes before. API docs Only then can we find the right structure:
var websocket = new WebSocket("ws://127.0.0.1:1215"); websocket.onopen = function (evt) { console.log("Connected websocket The server"); // This is the key point. After the channel is established, polling can be carried out very conveniently. setInterval(function() { if (websocket.bufferedAmount == 0) var data = {"holding": "eyJLQNDqj0y473pCJ6zjMTUyOTk5NzU1MgnVMQ==$d84XkeMCv7umajhMRiU"}; websocket.send(encodeMessage('loginCheck', data)); }, 50); }; // Monitor message body websocket.onmessage = function (evt) { console.log(decodeMessage(evt.data)) }; // Listen for shutdown messages websocket.onclose = function (evt) { console.log("websocket close"); }; //Listen for connection error messages websocket.onerror = function (evt) { console.log(evt); }; function decodeMessage(str) { return JSON.parse(str.substring(2))[1] || []; } function encodeMessage(event, data) { return JSON.stringify([ event, data ]) }
Swoole Extension Installation
Because the installation of swoole depends on the opening of php's sockets module.
- Install swoole
Intermediate error reporting requires installation of the following dependencies:
yum -y install gcc postgresql-devel gcc-c++
Download swoole extension source, install Installation steps Installation is enough.
Performance monitoring
View the current
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' TIME_WAIT 814 CLOSE_WAIT 1 FIN_WAIT1 1 ESTABLISHED 634 SYN_RECV 2 LAST_ACK 1
Three commonly used states are ESTABLISHED for communication, TIME_WAIT for active shutdown, CLOSE_WAIT for passive shutdown.
Delete process
View the number of processes
$ ps -eaf |grep "swoole" | grep -v "grep"| awk '{print $2}'|wc -l
Batch deletion process:
$ ps -eaf |grep "swoole" | grep -v "grep"| awk '{print $2}'|xargs kill -9
Restart the service.