Swoole server 120 lines of code to build a websocket chat room

Time:2020-3-12

swoole :http://www.swoole.com/
PHP’s asynchronous, parallel and high-performance network communication engine, written in pure C language, provides PHP’s asynchronous multi-threaded server, asynchronous TCP / UDP network client, asynchronous mysql, asynchronous redis, database connection pool, asynctask, message queue, millisecond timer, asynchronous file reading and writing, asynchronous DNS query. Swoole has built-in http / websocket server / client and http2.0 server.

Update 7-22: it has been redone with object-oriented and redis yesterday. Now it supports switching groups and private mail. It also optimizes other places. Video Demo: http://www.bilibilili.com/video… The list in the video has no normal refresh vulnerability. It has been fixed. GitHub: https://github.com/buff/buf

Demo address: http://www.buffge.xin/buffcha

end——

7-27 update

Today, the server was attacked. Maybe it was for testing or playing with me, and he didn’t succeed
He used my redis vulnerability to attack me, because I used a local visualization software to check the database of my server, so I
Open permissions and no encryption. After that, someone wants to upload a script to my server through redis. However, this script just runs repeatedly. It should be a
Hands on. He didn’t run this script because my redis permission is very low. He first added this sentence to my redis

Swoole server 120 lines of code to build a websocket chat room

Then I want this script to run in my server circularly. It’s really disgusting. I’m kind and open-source to show you how to play. I don’t have time to be so safe, but there are such scum coming to blackmail me
After a few days, I’ve finished my work, reopened the chat room and added new functions and styles. I’ll report it first. I’ve found his IP address and his group. It’s up to tx.bye~
——-end


I have been studying swoole last night. Finally, I have created a multi person chat room. Novices can have a look. Please go to the upper right corner for high play

Train of thought:

1: The user logs in to the main page, and puts the user into the database in the background (I won't redis memcached, etc.),
 The database format is

Swoole server 120 lines of code to build a websocket chat room

The first login only needs to insert FD

2: At this time, if you do not register, you cannot send information to the global
If registered, fill in the user name, AJAX requests to salt the user name to obtain the token
After successful registration, send token: 117fdba5e4d4050ed18ffd85ac86ed5b5d72dec6a271f4e4f6e03f4b957cd4: user_name: Zhang san123
If such a string is given to the server, the server will verify the token and name. If it is the correct token, then insert the user information into the database
And notify other websocket of new users online

3: Chat happily

Pits encountered on the road:

1: I was going to create an object in server.php,
        It stores the information and names of all visitors, etc., but it does not share this object. I think it can be cached with redis,
        I write here to play with MySQL, and there is no object-oriented
        2: I can't use session, but after I use it, there are various problems. I was prepared to use session to store user names
        There is no way for the background to come up with an encryption token and store it in the database,
        The specific token is like this

Swoole server 120 lines of code to build a websocket chat room
When registering, salt and sha256 are used to generate tokens, and then the same user name and salt are used to calculate when going to the swoole server for verification, to see if they are the same. If they are the same, it means that they are logged in correctly. I don’t know whether this method is good or not. Previously, only session was used, which was suddenly thought of last night

The logs on the server side are as follows:

Swoole server 120 lines of code to build a websocket chat room

The chat interfaces of the four users are as follows:

Swoole server 120 lines of code to build a websocket chat room

Swoole server 120 lines of code to build a websocket chat room

Swoole server 120 lines of code to build a websocket chat room

Swoole server 120 lines of code to build a websocket chat room

Server side code see comments. Write the slag don’t say, I am rookie

<?php

/*
  < Chen date = "2017-7-19" >
  < time = "00:46:16" >
  < Chen character = "buff" >
  < note = "" >
 */
if (php_sapi_name() !== 'cli') {
    Exit ("using cli mode");
}
$serv = new Swoole\Websocket\Server("192.168.1.109", 9501);
//Event triggered when callback function creates a websocket connection
$serv->on('Open', function($server, $req) {
    $mysqli = new mysqli("127.0.0.1", 'buffge', 'daimin', 'buffchat');
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    //Save the newly logged in user to MySQL
    $sql = "INSERT INTO `users` (`fd`) VALUES ('{$req->fd}')";
    $mysqli->query($sql);
    if ($mysqli->affected_rows !== 1) {
        Echo "failed to insert data! SQL = = {$SQL}";
    }
    Echo "new client connection:" $req - > FD. "Time:". Date ("y-n-j H: I: s"). "\ n";
    $userlist = "";
    //Check how many users are currently online
    $sql = "select `user_name` from users where `user_name`!=''";
    $res = $mysqli->query($sql);
    for ($i = 0; $i < $res->num_rows; $i++) {
        $result = $res->fetch_assoc();
        $userlist .= ('"' . $result['user_name'] . '",');
    }
    $userlist = substr($userlist, 0, strlen($userlist) - 1);
    //Notify user of current online user list
    $server->push($req->fd, "{\"code\":\"4\",\"users\":[{$userlist}]}");
    $res->free();
    $mysqli->close();
});
//Event triggered when a user's message is received
$serv->on('Message', function($server, $frame) {
    $mysqli = new mysqli("127.0.0.1", 'buffge', 'daimin', 'buffchat');
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    $sql = "select * from users where fd={$frame->fd}";
    $res = $mysqli->query($sql);
    $result = $res->fetch_assoc();
    //Get the name of the current sender
    $user_name = $result['user_name'];
    Echo "received message from client {$frame - > FD}:" $frame - > data. "" \ n ";
    //When the user is the first to register (the first 5 words of the sent statement are token)
    if (strpos($frame->data, 'token') === 0) {
        //If there is no token in the database
        if ($result['token'] === null) {
            $userData = explode(':', $frame->data);
            $hash = hash('sha256', 'daimin' . $userData[3]);
            if ($userData[1] == $hash) {
                $sql = "UPDATE `users` SET `token` = '{$hash}',`user_name` = '{$userData[3]}' WHERE `fd` = {$frame->fd}";
                $mysqli->query($sql);
                if ($mysqli->affected_rows !== 1) {
                    Echo "failed to update user information, SQL = = = {$SQL}";
                    $server - > push ($frame - > FD, "{" code \ ": \" 5 \ "," MES \ ": \" failed to update user information \ "}");
                }
                $server->push($frame->fd, "{\"code\":\"3\",\"user_name\":\"{$userData[3]}\"}");
                $res->free();
                $userlist = "";
                //Check how many users are currently online
                $sql = "select `user_name` from users where `user_name`!=''";
                $res = $mysqli->query($sql);
                for ($i = 0; $i < $res->num_rows; $i++) {
                    $result = $res->fetch_assoc();
                    $userlist .= ('"' . $result['user_name'] . '",');
                }
                $userlist = substr($userlist, 0, strlen($userlist) - 1);
                //Notify all users of the current list of online users
                foreach ($server->connections as $fd) {
                    $server->push($fd, "{\"code\":\"4\",\"users\":[{$userlist}]}");
                }
                Echo "new registered user {$UserData [3]} \ n";
            }
            else {
                $server - > push ($frame - > FD, "{" code \ ": \" 5 \ "," MES \ ": \" token error \ "}");
            }
        }
        //If you just send statements containing the token string, send them in groups
        else {
            foreach ($server->connections as $fd) {
                $server->push($fd, "{\"code\":\"2\",\"mes\":\"{$frame->data}\",\"user_name\":\"{$user_name}\"}");
            }
        }
    }
    //If it's not a registered user
    else {
        //If there is no token
        if ($result['token'] === null) {
            $server - > push ($frame - > FD, "{" MES ":" please log in first! \ "}");
            return;
        }
        //Convert newline to br
        $mes = nl2br($frame->data);
        //Format JSON
        $mes = str_replace("\n", "", $mes);
        //Mass message
        foreach ($server->connections as $fd) {
            $server->push($fd, "{\"code\":\"2\",\"mes\":\"{$mes}\",\"user_name\":\"{$user_name}\"}");
        }
    }
    $res->free();
    $mysqli->close();
});
//Event triggered when websocket is disconnected
$serv->on('Close', function($server, $fd) {
    $mysqli = new mysqli("127.0.0.1", 'buffge', 'daimin', 'buffchat');
    $charsetsql = "set names utf8";
    $mysqli->query($charsetsql);
    $sql = "DELETE FROM `users` WHERE `users`.`fd` = {$fd}";
    //Delete information when user exits
    $mysqli->query($sql);
    if ($mysqli->affected_rows !== 1) {
        Echo "failed to delete user information, SQL = = = {$SQL}";
    }
    Echo "client {$FD} disconnected \ n";
    $mysqli->close();
});

$serv->start();

Front end JS core

websocket.onmessage = function (evt) {
    console.log(evt.data);
    var data = JSON.parse(evt.data);
    //Judge business according to code
    switch (data.code) {
        //Personal message
        case '1':
            break;
            //Global message
        case '2':
            Var $time = bf_get_time(); // this is a get time function written by myself
            Var $MES = data.mes; // message content
            Var $user_name = data. User_name; // the person sending the message
            Var $who = $user_name = = = selfname? "Self": "other"; // format by name
            var $append = "<div class=\"mes_item\"><p><span class=\"user_name\">" + $user_name + " </span><time>" + $time + "</time></p><p class=\"message " + $who + "\">" + $mes + "</p></div>"
            $(".gui_content").append($append);
            var $cont_scrTop = $(".gui_content").scrollTop();
            var $list_height = $(".mes_item:last-of-type").height();
            $(".gui_content").animate({'scrollTop': $cont_scrTop + $list_height}, 100);
            break;
            //Notify registered user success message
        case '3':
            var $append = '<li>';
            $append += data.user_name;
            $append += '</li>';
            $(".user_list ul").append($append);
            $(". Gui_user"). HTML ("< p > welcome" + data. User_name + "< / P >");
            selfName = data.user_name;
            break;
            //Update the current online list deleted I didn't write~
        case '4':
            var $append = '';
            var $nowUser = $(".user_list ul li");
            outer:
                    for (var user in data.users) {
                for (var i = 0; i < $nowUser.length; i++) {
                    if ($nowUser[i].innerText === data.users[user]) {
                        continue outer;
                    }
                }
                console.log(data.users[user]);
                $append += '<li>';
                $append += data.users[user];
                $append += '</li>';
            }
            $(".user_list ul").append($append);
            break;
        case '5':
            alert(data.mes);
            break;
    }
    Console. Log ('received message from server: '+ data. Code);

};

When I was free, I used object-oriented writing to replace MySQL with redis. Now the code is a bit messy and some functions are not written. Because I was too sleepy, I wrote for 8 hours in a row

Can you tell me where to improve if you have a big idea? For example, how to verify users, which method to store them. Thank you

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]