Ethan Hughes

Ethan Hughes

1582341720

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P16)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P16): Polling to Maintain Long Connection Optimization

In the past two days, the internal Swoole ecosystem was quarreled due to the dispute of the official framework. I suddenly remembered that the Swoole chat room project also left two small tails, one was the optimization of long-connection polling, and the other was the optimization of user authentication under WebSocket communication. The two little tails were dealt with a few years ago, and the Swoole introduction to the actual combat tutorial was ended. Arguing is their business. We focus on the technology. What should we use?

Implementation scheme

First look at the long connection polling problem. In the previous tutorial, the long connection was maintained by continuous polling. Although the process can maintain the long connection, it seems to be no different from using Ajax polling when there was no Websocket. Can you handle all communication through a Websocket connection? Obviously, Socket.io itself provides support for this, we just need to follow its communication protocol to do just fine.

Since swooletw/laravel-swoole this project Socket.io client support is very friendly, and our project Websocket used by the client is Socket.io, so we modeled in the service end swooletw/laravel-swoole of the service-side implementation to do.

Code adjustment

Added SocketIOController

First, create a SocketIOController controller to handle client establishes a connection request Websocket:

php artisan make:controller SocketIOController

Just edit the generated app/Http/Controllers/SocketIOController.php code is as follows:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SocketIOController extends Controller
{
    protected $transports = ['polling', 'websocket'];

    public function upgrade(Request $request)
    {
        if (! in_array($request->input('transport'), $this->transports)) {
            return response()->json(
                [
                    'code' => 0,
                    'message' => 'Transport unknown',
                ],
                400
            );
        }

        if ($request->has('sid')) {
            return '1:6';
        }

        $payload = json_encode([
            'sid' => base64_encode(uniqid()), 
            'upgrades' => ['websocket'],  
            'pingInterval' => config('laravels.swoole.heartbeat_idle_time') * 1000,
            'pingTimeout' => config('laravels.swoole.heartbeat_check_interval') * 1000,
        ]);

        return response('97:0' . $payload . '2:40');
    }

    public function ok()
    {
        return response('ok');
    }
}

Response data field description

The returned data here may look a bit weird, this is a format that follows the Socket.io communication protocol so that the client can identify and do the correct processing. Let’s briefly introduce the data fields returned here

'97:0' . $payload . '2:40'

Wherein 97 represents the length of data returned, 0 represented open a new connection, and then return to load data $payload:

  • sid Indicates the session ID of this communication;
  • upgrades Indicates the type of protocol to be upgraded, here it is websocket;
  • pingInterval It represents a ping long interval, the heartbeat will be appreciated that the connection is maintained for a long time;
  • pingTimeout Indicates the timeout period of this connection. A long connection does not mean that it will never be destroyed. Otherwise, system resources can never be released. After the heartbeat connection is initiated, if there is no communication beyond this timeout period, the long connection will be automatically disconnected.

Later 2 it indicates that the client sent, the server should return packet containing the same data responds (the server returns the data 3 as a prefix to indicate a response, such as sending client 2probe returned from the server 3probe, the client sends 2, returned from the server 3, which is heartbeat connection), and finally 40 in 4 represents the message data, 0 representing a message to a stream of bytes returned.

New socket.io Route

Next, in routes/web.php the new routing point to two controllers two above methods:

Route::get('/socket.io', 'SocketIOController@upgrade');
Route::post('/socket.io', 'SocketIOController@ok');

Server connection establishment code adjustment

Finally, routes/websocket.php adjust the connection is established routing codes:

WebsocketProxy::on('connect', function (WebSocket $websocket, Request $request) {
    $websocket->setSender($request->fd);
});

Delete the send welcome message code, otherwise the default response message data format will be destroyed, causing the Socket.io client to fail to parse normally, and constantly initiate client connection establishment requests.

Client connection establishment code adjustment

Because here the inlet Websocket establish routing connections to adjust /socket.io, so it needs to adjust the front end of resources/js/socket.js the code:

import io from 'socket.io-client';
const socket = io('http://webchats.test');
export default socket;

Since Socket.io default path connection is established socket.io, it may be omitted corresponding to paththe configuration, transport the configuration may also be removed, because they can now return data transfer in accordance with the server protocol used is determined automatically.

Recompile the front-end resources:

npm run dev

Nginx configuration adjustment

The best, we must adjust the Nginx virtual host configuration, will be /ws adjusted to /socket.io:

location ^~ /socket.io {
    ...
}

Testing new Websocket communication

Refactor the Nginx container and restart all services:

docker-compose build nginx
docker-compose down
docker-compose up -d nginx mysql redis

Then enter the workspace container starts Websocket server:

cd webchat
bin/laravels start

Visit the chat room page again to log in, enter the room, chat, exit the room, etc. You can see in the developer console that all Websocket message flows are completed in one connection:

This is image title

This completes the code length remains connected by polling optimization, based Socket.io client to send the heartbeat connection is maintained long way connector (sending client 2, the server returns 3 the response as), of course, if the heartbeat connection is initiated If there is no communication after the timeout period, the long connection will be disconnected:

This is image title

There is another one here 5, which means that before switching the transmission protocol (such as upgrading to Websocket), it will test whether the server and client can communicate through this transmission. If the test is successful, the client will send an upgrade data packet and request the server to refresh the old transmission Cache and switch to the new transfer.

#laravel #swoole #vue

What is GEEK

Buddha Community

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P16)
Ethan Hughes

Ethan Hughes

1582164420

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P14)

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.

Front-end interaction code

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:

This is image title

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.

Image upload interface

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

Websocket server broadcast

Finally, we in routes/websocket.php the messagechannel 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.

Test image message release

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:

This is image title

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

Ethan Hughes

Ethan Hughes

1582067700

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P13): Send Text/Emotional Messages

Functional Overview

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:

This is image title

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:

This is image title

Front-end components

Next, let’s first look at the front-end component implementation.

Message sending logic

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 submessmethod:

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.

Message rendering logic

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).

Emoji components

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.

Back-end implementation

Writing API resource classes

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.

Modify history chat history interface

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:

This is image title

Restart the Swoole HTTP server, and you can see the rendered historical chat records in the front-end chat room room 1:

This is image title

You can view all historical messages by scrolling up and down.

Implement message sending and broadcasting functions

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.

Test live chat

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:

This is image title

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

Ethan Hughes

Ethan Hughes

1582341720

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P16)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P16): Polling to Maintain Long Connection Optimization

In the past two days, the internal Swoole ecosystem was quarreled due to the dispute of the official framework. I suddenly remembered that the Swoole chat room project also left two small tails, one was the optimization of long-connection polling, and the other was the optimization of user authentication under WebSocket communication. The two little tails were dealt with a few years ago, and the Swoole introduction to the actual combat tutorial was ended. Arguing is their business. We focus on the technology. What should we use?

Implementation scheme

First look at the long connection polling problem. In the previous tutorial, the long connection was maintained by continuous polling. Although the process can maintain the long connection, it seems to be no different from using Ajax polling when there was no Websocket. Can you handle all communication through a Websocket connection? Obviously, Socket.io itself provides support for this, we just need to follow its communication protocol to do just fine.

Since swooletw/laravel-swoole this project Socket.io client support is very friendly, and our project Websocket used by the client is Socket.io, so we modeled in the service end swooletw/laravel-swoole of the service-side implementation to do.

Code adjustment

Added SocketIOController

First, create a SocketIOController controller to handle client establishes a connection request Websocket:

php artisan make:controller SocketIOController

Just edit the generated app/Http/Controllers/SocketIOController.php code is as follows:

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SocketIOController extends Controller
{
    protected $transports = ['polling', 'websocket'];

    public function upgrade(Request $request)
    {
        if (! in_array($request->input('transport'), $this->transports)) {
            return response()->json(
                [
                    'code' => 0,
                    'message' => 'Transport unknown',
                ],
                400
            );
        }

        if ($request->has('sid')) {
            return '1:6';
        }

        $payload = json_encode([
            'sid' => base64_encode(uniqid()), 
            'upgrades' => ['websocket'],  
            'pingInterval' => config('laravels.swoole.heartbeat_idle_time') * 1000,
            'pingTimeout' => config('laravels.swoole.heartbeat_check_interval') * 1000,
        ]);

        return response('97:0' . $payload . '2:40');
    }

    public function ok()
    {
        return response('ok');
    }
}

Response data field description

The returned data here may look a bit weird, this is a format that follows the Socket.io communication protocol so that the client can identify and do the correct processing. Let’s briefly introduce the data fields returned here

'97:0' . $payload . '2:40'

Wherein 97 represents the length of data returned, 0 represented open a new connection, and then return to load data $payload:

  • sid Indicates the session ID of this communication;
  • upgrades Indicates the type of protocol to be upgraded, here it is websocket;
  • pingInterval It represents a ping long interval, the heartbeat will be appreciated that the connection is maintained for a long time;
  • pingTimeout Indicates the timeout period of this connection. A long connection does not mean that it will never be destroyed. Otherwise, system resources can never be released. After the heartbeat connection is initiated, if there is no communication beyond this timeout period, the long connection will be automatically disconnected.

Later 2 it indicates that the client sent, the server should return packet containing the same data responds (the server returns the data 3 as a prefix to indicate a response, such as sending client 2probe returned from the server 3probe, the client sends 2, returned from the server 3, which is heartbeat connection), and finally 40 in 4 represents the message data, 0 representing a message to a stream of bytes returned.

New socket.io Route

Next, in routes/web.php the new routing point to two controllers two above methods:

Route::get('/socket.io', 'SocketIOController@upgrade');
Route::post('/socket.io', 'SocketIOController@ok');

Server connection establishment code adjustment

Finally, routes/websocket.php adjust the connection is established routing codes:

WebsocketProxy::on('connect', function (WebSocket $websocket, Request $request) {
    $websocket->setSender($request->fd);
});

Delete the send welcome message code, otherwise the default response message data format will be destroyed, causing the Socket.io client to fail to parse normally, and constantly initiate client connection establishment requests.

Client connection establishment code adjustment

Because here the inlet Websocket establish routing connections to adjust /socket.io, so it needs to adjust the front end of resources/js/socket.js the code:

import io from 'socket.io-client';
const socket = io('http://webchats.test');
export default socket;

Since Socket.io default path connection is established socket.io, it may be omitted corresponding to paththe configuration, transport the configuration may also be removed, because they can now return data transfer in accordance with the server protocol used is determined automatically.

Recompile the front-end resources:

npm run dev

Nginx configuration adjustment

The best, we must adjust the Nginx virtual host configuration, will be /ws adjusted to /socket.io:

location ^~ /socket.io {
    ...
}

Testing new Websocket communication

Refactor the Nginx container and restart all services:

docker-compose build nginx
docker-compose down
docker-compose up -d nginx mysql redis

Then enter the workspace container starts Websocket server:

cd webchat
bin/laravels start

Visit the chat room page again to log in, enter the room, chat, exit the room, etc. You can see in the developer console that all Websocket message flows are completed in one connection:

This is image title

This completes the code length remains connected by polling optimization, based Socket.io client to send the heartbeat connection is maintained long way connector (sending client 2, the server returns 3 the response as), of course, if the heartbeat connection is initiated If there is no communication after the timeout period, the long connection will be disconnected:

This is image title

There is another one here 5, which means that before switching the transmission protocol (such as upgrading to Websocket), it will test whether the server and client can communicate through this transmission. If the test is successful, the client will send an upgrade data packet and request the server to refresh the old transmission Cache and switch to the new transfer.

#laravel #swoole #vue

Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

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

Ethan Hughes

Ethan Hughes

1582249320

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P15)

Building a Live Online Chat Room Based on Laravel + Swoole + Vue (P15): Implement User Avatar Upload Function

Avatar upload page entry

I missed the avatar upload function before, here to add, the avatar upload entrance is located in my-> modify avatar:

This is image title
This is image title

We just need to click upload the avatar on the avatar upload page, crop it, save the avatar, and then upload and save it to the user table.

Adjust the mu-icon-button component

However, this page is still reporting errors:

This is image title

Muse UI 3.0 version or removal of the mu-icon-button causes of elements support the following, we will adjust it for the mu-buttoncomponents.

Open the avatar upload page components resources/js/pages/Avatar.vue, mu-icon-button introduced located in Header the sub-components:

<Header></Header>

Open the file resources/js/components/Header/index.vue that defines the component and find the following template code:

<mu-appbar title="Title">
    <mu-icon-button icon="chevron_left" slot="left" @click="goback"/>
    <div class="center">
    </div>
    <mu-icon-button icon="expand_more" slot="right"/>
</mu-appbar>

All mu-icon-button components of adjusted mu-button components:

<mu-appbar title="Title">
    <mu-button icon slot="left" @click="goback">
        <mu-icon value="chevron_left"></mu-icon>
    </mu-button>
    <div class="center">
    </div>
    <mu-button icon slot="right">
        <mu-icon value="expand_more"></mu-icon>
    </mu-button>
</mu-appbar>

Then recompile the front-end resources:

npm run dev

In this way, you will not report an error when you visit the avatar upload page again.

Front page page avatar upload logic

Back to resources/js/pages/Avatar.vue upload picture files to the back-end logic defined in the postAvatar() method follows the code (click on the “Save Avatar” button will execute, and upload and crop operations are pure front-end behavior):

let files = new window.File([data], this.name, {type: this.type});
const formdata = new window.FormData();
formdata.append('file', files);
formdata.append('username', getItem('userid'));
const res = await this.$store.dispatch('uploadAvatar', formdata);

Front and rear ends, and many other interactive logic as previously defined, here we will form fields username adjusted api_token to backend API interface through which automatic authentication:

formdata.append('api_token', this.auth_token);

Accordingly, the need to add attributes in the calculation auth_token and reads the corresponding localStorage from the token field values:

computed: {
  ...mapState({
    userid: state => state.userInfo.userid,
    src: state => state.userInfo.src,
    auth_token: state => state.userInfo.token
  })
}

Then, based on Vuex defined uploadAvatar action initiates a request for back-end interface to upload picture, the corresponding interface calls defined resources/js/api/server.js in:

// Upload Avatar
postUploadAvatar: data => Axios.post('/file/avatar', data, {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    }
}),

This interface has not yet been implemented in the backend. Next, we will complete this interface at the Laravel backend.

Avatar upload backend interface implementation

In the routes/api.php new route file/avatar:

Route::middleware('auth:api')->group(function () {
    ...
    Route::post('/file/avatar', 'FileController@avatar');
}

Then FileController write controllers avatar ways:

public function avatar(Request $request)
{
    if (!$request->hasFile('file') || !$request->file('file')->isValid()) {
        return response()->json([
            'errno' => 500,
            'msg'   => 'Invalid parameters (avatar picture is empty or invalid)'
        ]);
    }
    $image = $request->file('file');
    $time = time();
    $filename = md5($time . mt_rand(0, 10000)) . '.' . $image->extension();
    $path = $image->storeAs('images/avatars/' . date('Y/m/d', $time), $filename, ['disk' => 'public']);
    if ($path) {
        // Save user avatar information to database
        $user = auth('api')->user();
        $user->avatar = Storage::disk('public')->url($path);
        $user->save();
        return response()->json([
            'errno' => 0,
            'data' => [
                'url' => $path
            ],
            'msg'   => 'Saved successfully'
        ]);
    } else {
        return response()->json([
            'errno' => 500,
            'msg'   => 'File upload failed, please try again'
        ]);
    }
}

And a tutorial written message picture upload interfaces are similar, but here is to save the avatar to the images/avatars directory, and then save the full path information to the user table avatar field, thus completing the user avatar updates.

Recompile the front-end resources and restart the Swoole HTTP server for the front-end and back-end code changes to take effect.

Test avatar upload function

Finally, let’s test the avatar upload function, forcibly refresh the avatar upload page, upload a new user avatar, and then save the avatar to call the upload avatar interface. After the save is successful, the following prompt box will pop up:

This is image title

After clicking OK, the page will jump to the user’s personal center page and display the new user avatar:

This is image title

At this point, the user avatar upload function is complete.

In this way, we have completed all the front-end page interaction functions of the chat room project. Next, we will spend two or three tutorials to optimize the code of the back-end Websocket server to complete the development of the Laravel + Swoole chat room project. Go to the next Laravel journey.

#laravel #swoole #vue