1581987660
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P12): Join and Exit Chat Room Function Implementation
Today, we continue the previous tutorial, and continue to introduce the Websocket server implementation that triggers the room entry event after the user enters the chat room, and the room exit event that is triggered when the room is exited.
We look into the realization of the room, in routes/websocket.php
the new code as follows:
WebsocketProxy::on('room', function (WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
// Get room ID from request data
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
// Reset user association with fd
Redis::command('hset', ['socket_id', $user->id, $websocket->getSender()]);
// Clear user unread messages in the room
$count = Count::where('user_id', $user->id)->where('room_id', $roomId)->first();
$count->count = 0;
$count->save();
// Add users to the specified room
$room = Count::$ROOMLIST[$roomId];
$websocket->join($room);
// Print log
Log::info($user->name . 'Enter the room:' . $room);
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
$user->src = $user->avatar;
if ($onelineUsers) {
$onelineUsers[$user->id] = $user;
Cache::forever($roomUsersKey, $onelineUsers);
} else {
$onelineUsers = [
$user->id => $user
];
Cache::forever($roomUsersKey, $onelineUsers);
}
// Broadcast messages to all users in the room
$websocket->to($room)->emit('room', $onelineUsers);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
First, make sure that the user is logged in (someone has suggested that user authentication can be handled by middleware, yes, I will optimize it uniformly later), and then make sure that the client passes the room number, otherwise it will not be able to communicate with the designated chat room Associated.
Next, we Redis through the HSET
stored associated data structures, user ID Websocket connection for subsequent connection Websocket may obtain the corresponding user ID, so that messages sent to it unread count:
Redis::command('hset', ['socket_id', $user->id, $websocket->getSender()]);
The user is then present within the Chat Room room unread message is set to 0, by App\Services\WebSocket\Websocket
the join
method of this room added:
$room = Count::$ROOMLIST[$roomId];
$websocket->join($room);
The default is based on a room here Redis
as a storage medium, you can config/laravels.php
be adjusted to SwooleTable
(currently only supports two drives):
'websocket' => [
'enable' => true,
'handler' => \App\Services\WebSocket\WebSocketHandler::class,
...
'drivers' => [
'default' => 'redis',
'table' => \App\Services\Websocket\Rooms\TableRoom::class,
'redis' => \App\Services\Websocket\Rooms\RedisRoom::class,
'settings' => [
'table' => [
...
],
'redis' => [
'server' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'persistent' => true,
],
'options' => [
//
],
'prefix' => 'swoole:',
],
],
],
Then, we update the online user information in the current room, we must save it to the cache, and finally broadcast the online user information to all users (including themselves) in the current room through the following code:
$websocket->to($room)->emit('room', $onelineUsers);
Thus, the client chat room pages resources/js/pages/Chat.vue
which can be transmitted by the data server code to receive:
socket.on('room', function (obj) {
that.$store.commit('setUsers', obj);
});
This is the implementation code for entering the room. Let’s look at exiting the room again.
Exiting the room is triggered when the client clicks the back button in the upper left corner of the chat room page:
goback () {
const obj = {
name: this.userid,
roomid: this.roomid,
api_token: this.auth_token,
};
socket.emit('roomout', obj);
...
},
Also contains user information, room information and authentication information in the service side we can respond to the client by writing the following routes in the roomout
request:
WebsocketProxy::on('roomout', function (WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
$room = Count::$ROOMLIST[$roomId];
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
if (!empty($onelineUsers[$user->id])) {
unset($onelineUsers[$user->id]);
Cache::forever($roomUsersKey, $onelineUsers);
}
$websocket->to($room)->emit('roomout', $onelineUsers);
Log::info($user->name . 'Exit the room: ' . $room);
$websocket->leave([$room]);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
Like entering a room, we need to ensure that the user is authenticated and the room number is not empty, then update the online user information (remove the current user), and finally leave the room with the following code and broadcast the updated user information to all online users on the client (Including yourself):
$websocket->to($room)->emit('roomout', $onelineUsers);
$websocket->leave([$room]);
Chat page on the client resources/js/pages/Chat.vue
, the server may be received by the code Websocket roomout
response:
socket.on('roomout', function (obj) {
that.$store.commit('setUsers', obj);
});
In addition to the user clicking the back button in the chat room, when the Websocket connection is disconnected, the user on the corresponding connection must also exit all rooms and go offline. For now logically disconnect and exit the room is the same, so you can share the same set of code we write disconnect
routing and reconstruction roomout
routing codes are as follows:
WebsocketProxy::on('roomout', function (WebSocket $websocket, $data) {
roomout($websocket, $data);
});
WebsocketProxy::on('disconnect', function (WebSocket $websocket, $data) {
roomout($websocket, $data);
});
function roomout(WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
$room = Count::$ROOMLIST[$roomId];
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
if (!empty($onelineUsers[$user->id])) {
unset($onelineUsers[$user->id]);
Cache::forever($roomUsersKey, $onelineUsers);
}
$websocket->to($room)->emit('roomout', $onelineUsers);
Log::info($user->name . 'Exit the room: ' . $room);
$websocket->leave([$room]);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
}
The client code does not need to make any adjustments. Let’s test the user entering and exiting the room.
In order to demonstrate the changes in the number of online users entering and exiting the room, we open a new browser (such as Firefox) to access the chat room application and register a new user test@xueyuanjun.com
, and then enter room 1 in the Chrome browser:
At this time, only one user chat room, the data table and then we counts
initialize the new user (ID = 2) unread message recording (hereinafter this initialization process may be automatic process):
Then login in Firefox browser and enter room 1:
You can see that there are two users in chat room 1, and then return to the Chrome browser chat room interface, you can see that it has also become two online users without refreshing the page:
This is because Websocket server sends a broadcast message, pushed to the room1
channel (room 1) of all clients.
Next, we exit room 1 in the Firefox browser, and then we can see that the number of online users has changed to 1 in the Chrome browser:
If you’re a developer panel by WS
viewing Websocket communication record label, then, you will see the joining and leaving the room in the Firefox browser will receive the corresponding broadcast message in the Chrome browser, and vice versa:
The above is an introduction to the front-end and back-end interaction of Websocket communication for joining and exiting the chat room. In the next tutorial, we will officially start writing the message sending implementation code.
#laravel #swoole #vue
1582067700
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13): Send Text/Emotional Messages
Sending messages supports a variety of formats, including ordinary text, emoticons, pictures, etc. Today we will introduce the most basic text and emoticons (Emoji is essentially a text message). Sending text messages requires entering text in the bottom text input box And click the Send button to send the message:
To send an emoticon (only Emoji is supported here) message, you need to click the emoticon icon to pop up a selection box, and then click to select an emoticon. The emoticon will be automatically rendered to the message text box and then sent with the text message:
Next, let’s first look at the front-end component implementation.
The front end interface components in the chat room resources/js/pages/Chat.vue
, the bottom of the corresponding transmitted message is a JavaScript code, click the send button, will call this submess
method:
submess() {
// Determine if the send message is empty
if (this.chatValue !== '') {
if (this.chatValue.length > 200) {
Alert({
content: 'Please enter less than 100 words'
});
return;
}
const msg = inHTMLData(this.chatValue); // Prevent xss
const obj = {
username: this.userid,
src: this.src,
img: '',
msg,
roomid: this.roomid,
time: new Date(),
api_token: this.auth_token
};
// Pass message
socket.emit('message', obj);
this.chatValue = '';
} else {
Alert({
content: 'the content can not be blank'
});
}
}
There will be a basic check in it. For example, the message content cannot be empty or exceed 100 characters. In addition, the input information will be processed to avoid XSS attacks. After all the above processes are completed, the message object will be initialized, and then Call the following code to send a message object through WebSocket communication:
socket.emit('message', obj);
After sending, clear the contents of the text box.
Rendering logic embedded in the page message from the sub-assembly Message
is achieved by two-way data binding:
<Message
v-for="obj in getInfos"
:key="obj._id"
:is-self="obj.userid === userid"
:name="obj.username"
:head="obj.src"
:msg="obj.msg"
:img="obj.img"
:mytime="obj.time"
:container="container"
></Message>
Here we noticed obj.username === userid
replaced with obj.userid === userid
, because the original VueChat implementations userid
and username
are equivalent, and we are here userid
and email
equivalence, is-self
attributes used to distinguish when rendering the message issued its own or someone else’s hair (his own hair in the right Side, others posted on the left).
The corresponding implementation of the Emoji selection box is as follows:
<div class="fun-li emoji">
<i class="icon iconfont icon-emoji"></i>
<div class="emoji-content" v-show="getEmoji">
<div class="emoji-tabs">
<div class="emoji-container" ref="emoji">
<div class="emoji-block" :style="{width: Math.ceil(emoji.people.length / 5) * 48 + 'px'}">
<span v-for="(item, index) in emoji.people" :key="index">{{item}}</span>
</div>
<div class="emoji-block" :style="{width: Math.ceil(emoji.nature.length / 5) * 48 + 'px'}">
<span v-for="(item, index) in emoji.nature" :key="index">{{item}}</span>
</div>
<div class="emoji-block" :style="{width: Math.ceil(emoji.items.length / 5) * 48 + 'px'}">
<span v-for="(item, index) in emoji.items" :key="index">{{item}}</span>
</div>
<div class="emoji-block" :style="{width: Math.ceil(emoji.place.length / 5) * 48 + 'px'}">
<span v-for="(item, index) in emoji.place" :key="index">{{item}}</span>
</div>
<div class="emoji-block" :style="{width: Math.ceil(emoji.single.length / 5) * 48 + 'px'}">
<span v-for="(item, index) in emoji.single" :key="index">{{item}}</span>
</div>
</div>
<div class="tab">
<!-- <a href="#hot"><span>Hot</span></a>
<a href="#people"><span>People</span></a> -->
</div>
</div>
</div>
</div>
The specific rendering logic is not the focus of this project. Interested students can go through the source code by themselves.
Run npm run dev
recompiled front-end resources for the changes to take effect.
Since the message rendering components Message
need to pass the message data will be rendered, and the front-end and back-end message object attribute messages
table can not be one to one, so we can write an API resource to do automatic conversion data structures between the two.
Prior to this, we first Message
define its relationship with the user model class:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Message extends Model
{
public $timestamps = false;
public function user()
{
return $this->belongsTo(User::class);
}
}
Then create by Artisan command Message
API resource corresponding model class:
php artisan make:resource MessageResource
The path corresponding to the generated command app/Http/Resources/MessageResource.php
to write conversion process toArray
is as follows:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class MessageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'userid' => $this->user->email,
'username' => $this->user->name,
'src' => $this->user->avatar,
'msg' => $this->msg,
'img' => $this->img,
'roomid' => $this->room_id,
'time' => $this->created_at
];
}
}
The target structure we transform must be consistent with the front-end message object attribute field names, so that the message data returned by the back-end can be rendered normally on the front-end.
Next, we can write before the interface chat history applications above MessageResource
do return JSON data structures automatically converted, open app/Http/Controllers/MessageController.php
, amend history
as follows:
use App\Http\Resources\MessageResource;
/**
* Get historical chat history
* @param Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function history(Request $request)
{
...
// Paging query messages
$messages = Message::where('room_id', $roomId)->skip($skip)->take($limit)->orderBy('created_at', 'asc')->get();
$messagesData = [];
if ($messages) {
// Automatic transformation of JSON data structures based on API resource classes
$messagesData = MessageResource::collection($messages);
}
// Return response
return response()->json([
'data' => [
'errno' => 0,
'data' => $messagesData,
'total' => $messageTotal,
'current' => $current
]
]);
}
Note: For the implementation principle of the API resource class, you can refer to the corresponding documents . We only use it here and do not introduce it in depth.
At this point, we are messages
filling some test data in the table:
Restart the Swoole HTTP server, and you can see the rendered historical chat records in the front-end chat room room 1:
You can view all historical messages by scrolling up and down.
Finally, we implement message sending and broadcasting functions based on Websocket.
Open the back-end Websocket routing file routes/websocket.php
, write the implementation code that receives the message and broadcasts it to all online users in the chat room:
use App\Message;
use Carbon\Carbon;
WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
// Get message content
$msg = $data['msg'];
$roomId = intval($data['roomid']);
$time = $data['time'];
// Message content or room number cannot be empty
if(empty($msg) || empty($roomId)) {
return;
}
// Record log
Log::info($user->name . 'in the room' . $roomId . 'Post message: ' . $msg);
// Save message to database
$message = new Message();
$message->user_id = $user->id;
$message->room_id = $roomId;
$message->msg = $msg;
$message->img = ''; // The picture field is temporarily blank
$message->created_at = Carbon::now();
$message->save();
// Broadcast messages to all users in the room
$room = Count::$ROOMLIST[$roomId];
$messageData = [
'userid' => $user->email,
'username' => $user->name,
'src' => $user->avatar,
'msg' => $msg,
'img' => '',
'roomid' => $roomId,
'time' => $time
];
$websocket->to($room)->emit('message', $messageData);
// Update the number of unread messages in this room for all users
$userIds = Redis::hgetall('socket_id');
foreach ($userIds as $userId => $socketId) {
// Update the number of unread messages per user and send them to the corresponding online users
$result = Count::where('user_id', $userId)->where('room_id', $roomId)->first();
if ($result) {
$result->count += 1;
$result->save();
$rooms[$room] = $result->count;
} else {
// If a record of the number of unread messages for a user does not exist, initialize it
$count = new Count();
$count->user_id = $user->id;
$count->room_id = $roomId;
$count->count = 1;
$count->save();
$rooms[$room] = 1;
}
$websocket->to($socketId)->emit('count', $rooms);
}
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
Implementation logic is very simple, to ensure that the user is authenticated, room number and content of the message is not empty premise, after obtaining the text message sent by the client (including Emoji expression), save it to the messages
table, and then broadcast to All users can be in the room, where we did not use MessageResource
to do automatically translate data structure, because the server can not get WebSocket Illuminate\Http\Request
instance, which can lead to JSON serialization error.
Note: The picture sending is also based on this message channel. We will implement the corresponding processing code in the next article.
Finally, we also updated the number of unread messages for users, stored them in a database, and sent them to all online users.
At this point, we have completed all the coding work, restart the Swoole server:
bin/laravels restart
Log in to different users in Chrome and Firefox browsers and enter the same chat room to start a live chat online:
Because it is based on Websocket communication, the page does not need to be refreshed to instantly get messages sent by the other party.
In the next tutorial, we will introduce the implementation of picture message sending. The source code for this project has been submitted to Github: https://github.com/nonfu/webchat.
#laravel #swoole #vue #online-chat
1582164420
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14): Send a Picture Message
In the last tutorial we demonstrated the release of text/emotional messages in chat rooms. Today we will look at how to post picture messages.
We started from the front end assembly, assembly in a chat room Chat.Vue
, the client core logic to send pictures located fileup
approach, we need to be adjusted to fit the rear end of the original code based interface Laravel + Swoole of:
fileup() {
const that = this;
const file1 = document.getElementById('inputFile').files[0];
if (file1) {
const formdata = new window.FormData();
formdata.append('file', file1);
formdata.append('api_token', this.auth_token);
formdata.append('roomid', that.roomid);
this.$store.dispatch('uploadImg', formdata);
const fr = new window.FileReader();
fr.onload = function () {
const obj = {
username: that.userid,
src: that.src,
img: fr.result,
msg: '',
roomid: that.roomid,
time: new Date(),
api_token: that.auth_token
};
socket.emit('message', obj);
};
fr.readAsDataURL(file1);
this.$nextTick(() => {
this.container.scrollTop = 10000;
});
} else {
console.log('Must have file');
}
},
When we click on the camera icon in the chat room, the image upload window will pop up:
After selecting the picture, it will call the above fileup
method to upload pictures.
It involves two logic: first calls the back-end interface to upload pictures based on the HTTP protocol and save the message to the messages
table, it will send a message to Websocket server after a successful upload, and then by Websocket server broadcasts a message to all online users.
The upload image corresponds to this line of code:
this.$store.dispatch('uploadImg', formdata);
The final call to the back-end interface code is located resources/js/api/server.js
in:
// upload image
postUploadFile: data => Axios.post('/file/uploadimg', data, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}),
We will write this interface in the backend shortly.
Sending a picture message based on Websocket corresponds to this line of code:
socket.emit('message', obj);
This is no different than sending a text message before the code is simply obj
there to add a imgfield only.
Next, we write an image upload interface on the Laravel backend.
In the routes/api.php
new route file/uploadimg
:
Route::middleware('auth:api')->group(function () {
...
Route::post('/file/uploadimg', 'FileController@uploadImage');
}
Then create the controller with Artisan commands FileController
:
php artisan make:controller FileController
In the newly generated file controller app/Http/Controllers/FileController.php
in preparation uploadImage
codes are as follows:
<?php
namespace App\Http\Controllers;
use App\Message;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class FileController extends Controller
{
public function uploadImage(Request $request)
{
if (!$request->hasFile('file') || !$request->file('file')->isValid() || !$request->has('roomid')) {
return response()->json([
'data' => [
'errno' => 500,
'msg' => 'Invalid parameter (room number/picture file is empty or invalid)'
]
]);
}
$image = $request->file('file');
$time = time();
$filename = md5($time . mt_rand(0, 10000)) . '.' . $image->extension();
$path = $image->storeAs('images/' . date('Y/m/d', $time), $filename, ['disk' => 'public']);
if ($path) {
// If the picture is uploaded successfully, the corresponding picture message is saved to the messages table
$message = new Message();
$message->user_id = auth('api')->id();
$message->room_id = $request->post('roomid');
$message->msg = ''; // Text message left blank
$message->img = Storage::disk('public')->url($path);
$message->created_at = Carbon::now();
$message->save();
return response()->json([
'data' => [
'errno' => 200,
'msg' => 'Saved successfully'
]
]);
} else {
return response()->json([
'data' => [
'errno' => 500,
'msg' => 'File upload failed, please try again'
]
]);
}
}
}
This mainly involves image upload and message saving logic. Because we will save the picture to the storage/public
next directory, in order to let the picture can be requested through the Web URL, you need to storage
create a soft catalog:
php artisan storage:link
Finally, we in routes/websocket.php
the message
channel complementary picture message processing logic:
WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
...
// Get message content
$msg = $data['msg'];
$img = $data['img'];
$roomId = intval($data['roomid']);
$time = $data['time'];
// Message content (including pictures) or room number cannot be empty
if((empty($msg) && empty($img))|| empty($roomId)) {
return;
}
// Record log
Log::info($user->name . 'in the room' . $roomId . 'Post message: ' . $msg);
// Save messages to the database (except for picture messages, because they were saved during the upload)
if (empty($img)) {
$message = new Message();
$message->user_id = $user->id;
$message->room_id = $roomId;
$message->msg = $msg; // Text message
$message->img = ''; // Picture message left blank
$message->created_at = Carbon::now();
$message->save();
}
// Broadcast messages to all users in the room
$room = Count::$ROOMLIST[$roomId];
$messageData = [
'userid' => $user->email,
'username' => $user->name,
'src' => $user->avatar,
'msg' => $msg,
'img' => $img,
'roomid' => $roomId,
'time' => $time
];
$websocket->to($room)->emit('message', $messageData);
...
Very simple, just add the picture message field uploaded by the client to the field of the previous broadcast message, without any other logic.
At this point, we can complete the front-end and back-end code for image message sending. Next, we test the sending of image messages on the chat room interface.
Before you start, recompile the front-end resources:
npm run dev
Make front-end code changes take effect. And restart Swoole HTTP and WebSocket server:
bin/laravels restart
Let the backend code changes take effect.
Then, open the chat room in Chrome and Firefox browsers, log in and enter the same room, you can send picture messages to each other in real time:
At this point, we have completed the main function of the chat room. Next, we will optimize the project code, especially the performance and elegance of the back-end WebSocket communication.
#laravel #swoole #vue #chat-room
1581987660
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P12): Join and Exit Chat Room Function Implementation
Today, we continue the previous tutorial, and continue to introduce the Websocket server implementation that triggers the room entry event after the user enters the chat room, and the room exit event that is triggered when the room is exited.
We look into the realization of the room, in routes/websocket.php
the new code as follows:
WebsocketProxy::on('room', function (WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
// Get room ID from request data
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
// Reset user association with fd
Redis::command('hset', ['socket_id', $user->id, $websocket->getSender()]);
// Clear user unread messages in the room
$count = Count::where('user_id', $user->id)->where('room_id', $roomId)->first();
$count->count = 0;
$count->save();
// Add users to the specified room
$room = Count::$ROOMLIST[$roomId];
$websocket->join($room);
// Print log
Log::info($user->name . 'Enter the room:' . $room);
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
$user->src = $user->avatar;
if ($onelineUsers) {
$onelineUsers[$user->id] = $user;
Cache::forever($roomUsersKey, $onelineUsers);
} else {
$onelineUsers = [
$user->id => $user
];
Cache::forever($roomUsersKey, $onelineUsers);
}
// Broadcast messages to all users in the room
$websocket->to($room)->emit('room', $onelineUsers);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
First, make sure that the user is logged in (someone has suggested that user authentication can be handled by middleware, yes, I will optimize it uniformly later), and then make sure that the client passes the room number, otherwise it will not be able to communicate with the designated chat room Associated.
Next, we Redis through the HSET
stored associated data structures, user ID Websocket connection for subsequent connection Websocket may obtain the corresponding user ID, so that messages sent to it unread count:
Redis::command('hset', ['socket_id', $user->id, $websocket->getSender()]);
The user is then present within the Chat Room room unread message is set to 0, by App\Services\WebSocket\Websocket
the join
method of this room added:
$room = Count::$ROOMLIST[$roomId];
$websocket->join($room);
The default is based on a room here Redis
as a storage medium, you can config/laravels.php
be adjusted to SwooleTable
(currently only supports two drives):
'websocket' => [
'enable' => true,
'handler' => \App\Services\WebSocket\WebSocketHandler::class,
...
'drivers' => [
'default' => 'redis',
'table' => \App\Services\Websocket\Rooms\TableRoom::class,
'redis' => \App\Services\Websocket\Rooms\RedisRoom::class,
'settings' => [
'table' => [
...
],
'redis' => [
'server' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD', null),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
'persistent' => true,
],
'options' => [
//
],
'prefix' => 'swoole:',
],
],
],
Then, we update the online user information in the current room, we must save it to the cache, and finally broadcast the online user information to all users (including themselves) in the current room through the following code:
$websocket->to($room)->emit('room', $onelineUsers);
Thus, the client chat room pages resources/js/pages/Chat.vue
which can be transmitted by the data server code to receive:
socket.on('room', function (obj) {
that.$store.commit('setUsers', obj);
});
This is the implementation code for entering the room. Let’s look at exiting the room again.
Exiting the room is triggered when the client clicks the back button in the upper left corner of the chat room page:
goback () {
const obj = {
name: this.userid,
roomid: this.roomid,
api_token: this.auth_token,
};
socket.emit('roomout', obj);
...
},
Also contains user information, room information and authentication information in the service side we can respond to the client by writing the following routes in the roomout
request:
WebsocketProxy::on('roomout', function (WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
$room = Count::$ROOMLIST[$roomId];
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
if (!empty($onelineUsers[$user->id])) {
unset($onelineUsers[$user->id]);
Cache::forever($roomUsersKey, $onelineUsers);
}
$websocket->to($room)->emit('roomout', $onelineUsers);
Log::info($user->name . 'Exit the room: ' . $room);
$websocket->leave([$room]);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
Like entering a room, we need to ensure that the user is authenticated and the room number is not empty, then update the online user information (remove the current user), and finally leave the room with the following code and broadcast the updated user information to all online users on the client (Including yourself):
$websocket->to($room)->emit('roomout', $onelineUsers);
$websocket->leave([$room]);
Chat page on the client resources/js/pages/Chat.vue
, the server may be received by the code Websocket roomout
response:
socket.on('roomout', function (obj) {
that.$store.commit('setUsers', obj);
});
In addition to the user clicking the back button in the chat room, when the Websocket connection is disconnected, the user on the corresponding connection must also exit all rooms and go offline. For now logically disconnect and exit the room is the same, so you can share the same set of code we write disconnect
routing and reconstruction roomout
routing codes are as follows:
WebsocketProxy::on('roomout', function (WebSocket $websocket, $data) {
roomout($websocket, $data);
});
WebsocketProxy::on('disconnect', function (WebSocket $websocket, $data) {
roomout($websocket, $data);
});
function roomout(WebSocket $websocket, $data) {
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
if (empty($data['roomid'])) {
return;
}
$roomId = $data['roomid'];
$room = Count::$ROOMLIST[$roomId];
// Update online user information
$roomUsersKey = 'online_users_' . $room;
$onelineUsers = Cache::get($roomUsersKey);
if (!empty($onelineUsers[$user->id])) {
unset($onelineUsers[$user->id]);
Cache::forever($roomUsersKey, $onelineUsers);
}
$websocket->to($room)->emit('roomout', $onelineUsers);
Log::info($user->name . 'Exit the room: ' . $room);
$websocket->leave([$room]);
} else {
$websocket->emit('login', 'Login to enter chat room');
}
}
The client code does not need to make any adjustments. Let’s test the user entering and exiting the room.
In order to demonstrate the changes in the number of online users entering and exiting the room, we open a new browser (such as Firefox) to access the chat room application and register a new user test@xueyuanjun.com
, and then enter room 1 in the Chrome browser:
At this time, only one user chat room, the data table and then we counts
initialize the new user (ID = 2) unread message recording (hereinafter this initialization process may be automatic process):
Then login in Firefox browser and enter room 1:
You can see that there are two users in chat room 1, and then return to the Chrome browser chat room interface, you can see that it has also become two online users without refreshing the page:
This is because Websocket server sends a broadcast message, pushed to the room1
channel (room 1) of all clients.
Next, we exit room 1 in the Firefox browser, and then we can see that the number of online users has changed to 1 in the Chrome browser:
If you’re a developer panel by WS
viewing Websocket communication record label, then, you will see the joining and leaving the room in the Firefox browser will receive the corresponding broadcast message in the Chrome browser, and vice versa:
The above is an introduction to the front-end and back-end interaction of Websocket communication for joining and exiting the chat room. In the next tutorial, we will officially start writing the message sending implementation code.
#laravel #swoole #vue
1600583123
In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.
Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.
This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.
Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.
Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.
“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You
#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js
1582423080
Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P17-Final): Websocket Communication User Authentication Logic Optimization
Previously, in Websocket communication, user authentication was very rude, that is, each time it obtained from the request field api_token
, and then determined whether the corresponding user record exists on the server:
if (!empty($data['api_token']) && ($user = User::where('api_token', $data['api_token'])->first())) {
...
}
We can optimize this certification process like this:
$websocket->loginUsing()
the method provided in this connection on a user ID, a user authentication bound state;$websocket->getUserId()
obtaining the user ID authentication on the connection method;$websocket->logout()
the method binding canceled user authentication status.Let’s take a look at how to do it.
Let socket.io
relevant routes through Laravel certified middleware auth:api
filtration, unauthenticated users can not access, which can be SocketIOController
achieved constructor controller:
public function __construct()
{
$this->middleware('auth:api');
}
Then adjust the code corresponding to the middleware (in app/Http/Middleware/Authenticate.php
), to throw unauthenticated user authentication error alarm, not a jump to login
route:
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
throw new AuthorizationException("Websocket connection can only be established after login");
}
}
Next, we went to the front, Socket.io need to specify the client establishes a connection api_token
request field, so that the server can read and authenticate automatically, modify the socket.js
code as follows:
...
import store from "./store";
let api_token = store.state.userInfo.token;
const socket = io('http://webchats.test?api_token=' + api_token);
...
Finally, open routes/websocket.php
and adjust the user authentication related code as follows:
WebsocketProxy::on('connect', function (WebSocket $websocket, Request $request) {
// Send welcome message
$websocket->setSender($request->fd);
// Binding authentication user information when establishing a connection
$websocket->loginUsing(auth('api')->user());
});
WebsocketProxy::on('room', function (WebSocket $websocket, $data) {
if ($userId = $websocket->getUserId()) {
$user = User::find($userId);
...
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
WebsocketProxy::on('message', function (WebSocket $websocket, $data) {
if ($userId = $websocket->getUserId()) {
$user = User::find($userId);
...
} else {
$websocket->emit('login', 'Login to enter chat room');
}
});
...
WebsocketProxy::on('disconnect', function (WebSocket $websocket, $data) {
roomout($websocket, $data);
$websocket->logout();
});
...
Because here Websocket
by default only provides a getUserId
method does not provide getUser
a method, so even after the query again to authenticate the user to obtain more information, you may also be based on the underlying loginUsingUserId
and getUserId
write their own implementation loginUsingUser
and getUser
the realization of logic, there is no longer expand in detail. In addition, since the user Websocket connection can be established, so after the certification websocket.php
of the login
realization seems to be the need for reconstruction, interested students can themselves to think and realize, college is king here when the initiate, provide some general ideas and programs, more details left to your own Go explore.
The introduction to Swoole from the beginning to the actual combat is briefly introduced here. Next, the academy will continue to Laravel from the entry to the mastery of the writing of the tutorial, and to focus more on the use and promotion of Golang. Of course, the PHP fpm framework will first promote Laravel. As for the subsequent scale of microservice refactoring, Golang is recommended (the Java party can bypass it).
#laravel #swoole #vue