swoole and websocket Simple Chat Room Development

  • 2021-08-16 23:29:31
  • OfStack

First of all, I want to talk about some habits of writing code. First, any configurable parameters or variables should be written to an config file. Second, 1 in the code must have log records and perfect error reporting and record error reporting. To get down to business, swoole should be something that every phper must know. It claims to redefine php. This chat room takes advantage of the high concurrency and asynchronous non-blocking characteristics of swoole to improve the performance of the program.

First, define an swoole_lock and an swoole_websocket_server, and configure the parameters. Details of the specific parameters can be viewed in swoole official website.


public function start()
{
  $this->lock = new swoole_lock(SWOOLE_MUTEX);                          

  //  Lock operation on file or array, synchronization has been reached 
  $this->server = new swoole_websocket_server($this->addr, $this->port);    

  // swoole Offered Websocket Server
  $this->server->set(array(
   'daemonize' => 0,
   'worker_num' => 4,
   'task_worker_num' => 10,
   'max_request' => 1000,
   'log_file' => ROOT_PATH . 'storage\\logs\\swoole.log'  

  // swoole Log path, must be an absolute path 
  ));

  $this->server->on('open', array($this, 'onOpen'));
  $this->server->on('message', array($this, 'onMessage'));
  $this->server->on('task', array($this, 'onTask'));
  $this->server->on('finish', array($this, 'onFinish'));
  $this->server->on('close', array($this, 'onClose'));

     //  Startup service 
  $this->server->start();
}

When there is a client link, simply record the client information.


public function onOpen($server, $request)
  {
   $message = array(
    'remote_addr' => $request->server['remote_addr'],
    'request_time' => date('Y-m-d H:i:s', $request->server['request_time'])
   );
   write_log($message);
  }

When a client sends information, the information is processed.


public function onMessage($server, $frame)
  {
   $data = json_decode($frame->data);

   switch ($data->type) {
    case 'init':
    case 'INIT':
     $this->users[$frame->fd] = $data->message; , 

   //  Record the information of each link, and don't try to print it out, because you can only see your own link information 
     $message = ' Welcome ' . $data->message . ' Joined the chat room ';
     $response = array(
      'type' => 1, // 1 Represents system messages, 2 Chat on behalf of users 
      'message' => $message
     );
     break;
    case 'chat':
    case 'CHAT':
     $message = $data->message;
     $response = array(
      'type' => 2, // 1 Represents system messages, 2 Chat on behalf of users 
      'username' => $this->users[$frame->fd],
      'message' => $message
     );
     break;
    default:
     return false;
   }
            
         //  Give the information to task Deal with 
   $this->server->task($response);
  }

  public function onTask($server, $task_id, $from_id, $message)
  {
         //  Iterate all the client links and push the message over. (If you try to put  $this->server->connections  Print it out, then you will find that it is empty. But it was used at that time  foreach  It does work when de-looping.) 
   foreach ($this->server->connections as $fd) {
    $this->server->push($fd, json_encode($message));
   }
   $server->finish( 'Task' . $task_id . 'Finished' . PHP_EOL);
  }

Finally, when the client breaks the link, the client information is deleted synchronously by using the lock mechanism, and the log is recorded.


public function onClose($server, $fd)
  {
   $username = $this->users[$fd];
   //  Release the client and synchronize with the lock 
   $this->lock->lock();
   unset($this->users[$fd]);
   $this->lock->unlock();

   if( $username ) {
    $response = array(
     'type' => 1, // 1 Represents system messages, 2 Chat on behalf of users 
     'message' => $username . ' Leaving the chat room '
    );
    $this->server->task($response);
   }


   write_log( $fd . ' disconnected');
  }

The server is finished, the following is the client, very simple, only need to use websocket link on ok!


// websocket
  let address = 'ws://<?php echo CLIENT_CONNECT_ADDR . ':' . CLIENT_CONNECT_PORT ?>';
  let webSocket = new WebSocket(address);
  webSocket.onerror = function (event) {
   alert(' Server connection error, please try again later ');
  };
  webSocket.onopen = function (event) {
   if(!sessionStorage.getItem('username')) {
    setName();
   }else {
    username = sessionStorage.getItem('username')
    webSocket.send(JSON.stringify({
     'message': username,
     'type': 'init'
    }));
   }
  };
  webSocket.onmessage = function (event) {
   console.log(event);
   let data = JSON.parse(event.data);
   if (data.type == 1) {
    $('#chat-list2').append('<li class="ui-border-tb"><span class="username"> System message :</span><span class="message">' + data.message + '</span></li>');
   } else if (data.type == 2) {
    $('#chat-list2').append('<li class="ui-border-tb"><span class="username">' + data.username + ':</span><span class="message">' + data.message + '</span></li>');
   }

  };
  webSocket.onclose = function (event) {
   alert(' Come on, the servers are turned off ');
  };

Detailed code can go to my github download


Related articles: