Workerman is an open-source, high-performance asynchronous PHP socket instant messaging framework.
What is a Workerman?
Workerman is an open-source, high-performance asynchronous PHP socket instant messaging framework. It supports high concurrency and ultra-high stability, and is widely used in the development of mobile app, mobile communication, wechat small program, mobile game server, online games, PHP chat room, hardware communication, smart home, Internet of cars, Internet of things and other fields. Supports TCP long connections, Websocket, HTTP, and custom protocols. With asynchronous Mysql, asynchronous Redis, asynchronous Http, MQTT Internet of Things client, asynchronous message queue and many other high-performance components.
Let’s set up and configure Workerman
Step 1: Write down extension Composer for workerman.
"workerman/gateway-worker": "^ 3.0"."workerman/gatewayclient": "^ 3.0"."workerman/workerman": "^ 3.5".Copy the code
Step 2: Go to the official website to download all the demo, and then put it in our project directory, I used the Laravel framework here
In the picture I’ll put the whole project in the HTTP/Controller/Workerman.
Step 3: We need to change the reference part of the following three files to the following. Otherwise, a path error is reported
require_once __DIR__ . '/.. /.. /.. /.. /.. /vendor/autoload.php';
Copy the code
After modification, we can run the corresponding startup file directly in Liunx
php start.php start -d
Copy the code
If you are in window, double-click start_for_win.bat to run it
Once it runs successfully, you should see the following screen
At this point, we have built a communication environment based on Workerman. Then we can develop according to our own project requirements. Here is a key explanation to you. All of our chat logic is modified in the directory events.php.
Here are some of the code I wrote for you. How to implement a chat function
Event.php
<? php /** * This file is part of workerman. * * Licensed under The MIT License * For full copyright and license information, please see the MIT-LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @author walkor<[email protected]> * @copyright walkor<[email protected]> * @link http://www.workerman.net/ * @license http://www.opensource.org/licenses/mit-license.php MIT License * / / * * * business code used in the detection of infinite loop or long time blocking issues such as business card * if found dead, can be the followingdeclareOpen (remove // comments) and execute PHP start. PHP reload * then watch workerman.log for a while to see if process_timeout is abnormal */ //declare(ticks=1); /** * onMessage onClose */ use \GatewayWorker\Lib\Gateway; Class Events {/** ** when the client connects * @param$client_idThis ID is automatically generated for the gatewayworker ID */ public staticfunction onConnect($client_id)
{
Gateway::sendToClient($client_id, json_encode(array(
'type'= >'init'.'client_id'= >$client_id))); } /** * @param int when there is a message$client_id
* @param mixed $message
*/
public static function onMessage($client_id.$message)
{
// debug
echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n"; // The client passes json data$message_data = json_decode($message.true);
if(!$message_data)
{
return; } // Execute different service switches according to the type ($message_data['type']) {// Heartbeat between client and servercase 'pong':
return; // Client login message format: {type:login, name:xx, room_id:1}, add to the client, broadcast to all clients xx enter the chat roomcase 'login'Check if there is a room numberif(! isset($message_data['room_id']))
{
throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message"); } // Put the room nickname into session$room_id = $message_data['room_id'];
$client_name = htmlspecialchars($message_data['client_name']);
$_SESSION['room_id'] = $room_id;
$_SESSION['client_name'] = $client_name; // Get a list of all users in the room$clients_list = Gateway::getClientSessionsByGroup($room_id);
foreach($clients_list as $tmp_client_id= >$item)
{
$clients_list[$tmp_client_id] = $item['client_name']; } / /$clients_list[$client_id] = $client_name; // Relay to all clients in the current room, xx enters the chat room message {type:login, client_id:xx, name:xx}
$new_message = array('type'= >$message_data['type'].'client_id'= >$client_id.'client_name'=>htmlspecialchars($client_name), 'time'=>date('Y-m-d H:i:s'),'to'= >$message_data['to'].'room_id'= >$message_data['room_id'].'from'= >$message_data['from'].'tag'= >$message_data['tag']);
Gateway::sendToGroup($room_id, json_encode($new_message));
Gateway::joinGroup($client_id.$room_id); // Send a list of users to the current user$new_message['client_list'] = $clients_list;
Gateway::sendToCurrentClient(json_encode($new_message));
return; // message: {type:say, to_client_id:xx, content:xx}
case 'say': // Invalid requestif(! isset($_SESSION['room_id']))
{
throw new \Exception("\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}");
}
$room_id = $_SESSION['room_id'];
$client_name = $_SESSION['client_name']; / / / / private chatif($message_data['to_client_id'] != 'all'/ / {/ /$new_message = array(
// 'type'= >'say', / /'from_client_id'= >$client_id, / /'from_client_name'= >$client_name, / /'to_client_id'= >$message_data['to_client_id'], / /'content'= >" says to you: ".nl2br(htmlspecialchars($message_data['content')), / /'time'=>date('Y-m-d H:i:s'),
// );
// Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));
// $new_message['content'] = "< b > for you".htmlspecialchars($message_data['to_client_name'])."Say: < / b >".nl2br(htmlspecialchars($message_data['content']));
// return Gateway::sendToCurrentClient(json_encode($new_message));
// }
$new_message = array(
'type'= >'say'.'from_client_id'= >$client_id.'from_client_name'= >$client_name.'to_client_id'= >'all'.'content'=>nl2br(htmlspecialchars($message_data['content')),'time'=>date('Y-m-d H:i:s'));return Gateway::sendToGroup($room_id ,json_encode($new_message)); }} /** ** when the client is disconnected * @paraminteger $client_idClient ID */ public staticfunction onClose($client_id)
{
// debug
echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id onClose:''\n"; // Delete from the client list of the roomif(isset($_SESSION['room_id']) {$room_id = $_SESSION['room_id'];
$new_message = array('type'= >'logout'.'from_client_id'= >$client_id.'from_client_name'= >$_SESSION['client_name'].'time'=>date('Y-m-d H:i:s'));
Gateway::sendToGroup($room_id, json_encode($new_message)); }}}Copy the code
Client page
<! DOCTYPE html> <html lang="en">
<head>
<meta charset="UTF-8"With {{> < title >$to->name}} dialog </title> <scripttype="text/javascript" src="{{asset('js')}}/swfobject.js"></script>
<script type="text/javascript" src="{{asset('js')}}/web_socket.js"></script>
<script type="text/javascript" src="{{asset('js')}}/jquery.min.js"></script>
<link href="{{asset (' CSS ')}} / jquery - sinaEmotion - 2.1.0. Min. CSS" rel="external nofollow" rel="stylesheet">
<link href="{{asset('css')}}/bootstrap.min.css" rel="external nofollow" rel="stylesheet">
<link href="{{asset('css')}}/style.css" rel="external nofollow" rel="stylesheet">
<script type="text/javascript" src="{{asset (' js')}} / jquery - sinaEmotion - 2.1.0. Min. Js." "></script>
{{--<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>--}}
</head>
<style>
#sinaEmotion {
z-index: 999;
width: 373px;
padding: 10px;
display: none;
font-size: 12px;
background: #fff;
overflow: hidden;
position: absolute;
border: 1px solid #e8e8e8;top: 100px; Left: 542.5 px; } </style> <body onload="connect();" style="margin: auto; text-align: center;">
<div style="margin: auto;">
<div style="border: 1px solid red; height: 40px; width: 500px; margin: auto;"> {{-- dialog header --}} <div> <div style="width: 80px; height: 40px; border: 1px solid blue; float: left">
<img src="{{$to->heading}}" width="80px" height="40px">
</div>
<div style="width: 150px; height: 40px; border: 1px solid blue; float: left">
{{$to- > name}} < / div > < / div > {{- / / dialogue window contents -}} < div class ="content" style="width: 500px; height: 400px; border: 1px solid green; margin-top: 40px; overflow-y: auto"> <div style= "max-width: 100%; clear: both; min-height: 1em"min-height: 50px; margin-top: 10px;">--}}
{{--<div style="width: 50px; height: 50px; border: 1px solid red; margin-left:10px; float: left">--}}
{{--<img src="{{$to->heading}}" width="50px" height="50px">--}}
{{--</div>--}}
{{--<div style="border: 1px solid red; float: left; min-height: 50px"> dsadsadsadsadsa < / div > --}} {{- < / div > --}} {{- my head with words -}} {{- < div style ="min-height:50px; margin-top: 10px;">--}}
{{--<div style="width: 50px; height: 50px; border: 1px solid red; margin-left:10px; float: right">--}}
{{--<img src="{{$from->heading}}" width="50px" height="50px">--}}
{{--</div>--}}
{{--<div style="border: 1px solid red; float: right; min-height: 50px"> dsadsadsadsadsa < / div > --}} {{- < / div > --}} < / div > {{- send dialogue window -}} < form onsubmit ="return onSubmit(); return false;" id="ajaxfrom">
<input type="hidden" name="to" value="{{$to->id}}">
<input type="hidden" name="from" value="{{$from->id}}">
<input type="hidden" name="room_id" value="{{$room}}">
<input type="hidden" name="tag" value="{{$tag}}">
<textarea id="textarea" name="content" class="Input_text" style="margin: 0px; width: 501px; height: 213px;"></textarea>
<div class="say-btn">
<input type="button" class="btn btn-default face pull-left" value="Expression" />
<button type="submit" class="btn btn-default"</button> </div> </form>$room}}
</div>
</div>
</body>
</html>
<script type="text/javascript">
if (typeof console == "undefined") { this.console = { log: function(msg) { } }; } // If the browser does not support websocket, the flash will automatically emulate the WebSocket protocol, which is transparent to the developer"/swf/WebSocketMain.swf"; // Enable flash websocket debug WEB_SOCKET_DEBUG =true;
var ws, name, client_list={};
var to_client_id=""; // Connect server initialization functionfunction connect() {// create websocket can then be replaced with the corresponding server address ws = new websocket ()"ws://"+document.domain+": 7272"); // When the socket connection is opened, enter the username ws. Onopen = onopen; Ws. onMessage = onMessage; ws. onMessage = onMessage; // When a connection is lost, call the connection method to try to reconnect with ws-onclose =function() {
console.log("Connection closed. Timed reconnection."); connect(); }; // If the operation fails, return exception ws.onError =function() {
console.log("Error occurred"); }; $.post($.post)"/get_record", { "room":"{{$room}}" },
function(msg){
$.each(msg,function(v,k) { console.log(k); / / determineif(k.tag! ="{{$tag}}"){
$(".content").append(
'
'
+
'
'
+
'<img src="{{$to->heading}}" width="50px" height="50px">'+
'</div>'+
'<div style="border: 1px solid red; float: left; min-height: 50px" >'+k.content+'</div>'+
'<div>'
).parseEmotion();
}else{$(".content").append(
'
'
+
'
'
+
'<img src="{{$from->heading}}" width="50px" height="50px">'+
'</div>'+
'<div style="border: 1px solid red; float: right; min-height: 50px" >'+k.content+'</div>'+
'<div>').parseEmotion(); }})}); } // Send login information when the connection is establishedfunction onopen()
{
var login_data='{"type":"login","client_name":"{{$from->name}}","room_id":"{{$room}}","to":"{{$to->id}}","from":"{{$from->id}}","tag":" {{$tag}}"}';
ws.send(login_data);
console.log('Login successful'} // When the server sends a messagefunction onmessage(e)
{
var data = JSON.parse(e.data);
switch(data['type']){// The server ping the heartbeat of the clientcase 'ping':
ws.send('{"type":"pong"}');
break; // Log in to update the user listcase 'login': // Save the required send ID to the local to_client_id variablefor(var p in data['client_list']){
to_client_id=p;
}
console.log(to_client_id);
break; / / to speakcase 'say':
console.log(data);
say(data['from_client_id'], data['from_client_name'], data['content'], data['time']);
break; // The user exits to update the user listcase 'logout':
console.log(data);
break;
case 'init'// Here you can send Ajax to bind different user ids and client console.log(data);break; }} // Submit the dialogfunction onSubmit() {// Check whether there are more than 20 records in the current sessiontrue; $. Ajax ({url:"/check_count".type: "post",
async:false,
// cache: false,
// contentType: false,
// processData: false,
data:{
'room':"1",
},
success: function (msg) {
if(msg>10){
alert('The current conversation has exceeded the number of times, please purchase the corresponding service')
count=false; }}});if(count){
var neirong=$("#textarea").val().replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r'); Var FM =$("# ajaxFrom ")[0]; var formData = new FormData(fm); $.ajax({ url: "/record", type: "post", cache: false, contentType: false, processData: false, data: formData, beforeSend:function(){ }, success: function (msg) { if(msg.code=="0"){ ws.send('{"type":"say"."to_client_id":"all"."to_client_name":"{{$to->name}}"."content":"'+neirong+'"}'); $("#textarea").val(""); $("#textarea").focus(); }else{ } } }); } return false; } function say(from_client_id, from_client_name, content, If (" {{$from->name}}" == from_client_name){$(".content").append(')<div style="min-height: 50px; margin-top: 10px;">'+'<div style="width: 50px; height: 50px; border: 1px solid red; margin-left:10px; float: right">'+'<img src="{{$from->heading}}" width="50px" height="50px">'+'</div>'+'<div style="border: 1px solid red; float: right; min-height: 50px" >'+content+'</div>'+'<div>' ).parseEmotion(); }else{ $(".content").append( '<div style="min-height: 50px; margin-top: 10px;">'+'<div style="width: 50px; height: 50px; border: 1px solid red; margin-left:10px; float: left">'+'<img src="{{$to->heading}}" width="50px" height="50px">'+'</div>'+'<div style="border: 1px solid red; float: left; min-height: 50px" >'+content+'</div>'+'<div>' ).parseEmotion(); } // $("#dialog").append('<div class="speech_item"><img src="http://lorempixel.com/38/38/?'+from_client_id+'" class="user_icon" /> '+from_client_name+' <br> '+time+'<div style="clear:both;"></div><p class="triangle-isosceles top">'+content+'</p> </div>').parseEmotion(); } $(function(){// select_client_id = 'all'; $("#client_list"). Change (function(){select_client_id = $("#client_list") option:selected").attr("value"); }); // select $(').face').click(function(event){ $(this).sinaEmotion(); event.stopPropagation(); }); }); // document.write('<meta name="viewport" content="width=device-width,initial-scale=1">'); $("textarea").on(" keyDown ", function(e) {if(e.keycode === 13&&! e.ctrlKey) { e.preventDefault(); $('form').submit(); return false; If (e.keyCode === 13&&e.trlkey){$(this). Val (function(I,val){return val + "\n"; }); }}); Copy the code
These two code snippets are essentially the core snippets of the main run. The parameters of other frameworks need to be adjusted and optimized according to the documentation. The Workerman-based chat function demo has been built.