Socket.io

Socket.io is a Javascript library that enables real-time, bidirectional and event-based communication.

Sometimes we hear that socket.io implements a web socket communication but is NOT true.

Socket.io uses the WebSocket protocol to provide the interface. Generally, it is divided into two parts, both WebSocket vs Socket.io are event-driven libraries

  • Client Side: it is the library that runs on client side
  • Server Side: it is the library for Node.js

Server Side

mkdir chatserver
cd chatserver
touch index.js
// index.js

let app = require('express')();
let http = require('http').Server(app);
let io = require('socket.io')(http);
 
io.on('connection', (socket) => {
  
  socket.on('disconnect', function(){
    io.emit('users-changed', {user: socket.nickname, event: 'left'});   
  });
 
  socket.on('set-nickname', (nickname) => {
    socket.nickname = nickname;
    io.emit('users-changed', {user: nickname, event: 'joined'});    
  });
  
  socket.on('add-message', (message) => {
    io.emit('message', {text: message.text, from: socket.nickname, created: new Date()});    
  });
});
 
var port = process.env.PORT || 3001;
 
http.listen(port, function(){
   console.log('listening in http://localhost:' + port);
});
npm install --save express socket.io
npm init
node index.js

Client Side

Let's start a chatroom on Ionic

git clone https://github.com/texano00/ionic3-chat-socketio.git
cd ionic3-chat-socketio
npm i
ionic serve // or ionic cordova run android

Focus on socket.io usage

import { Component, ViewChild } from '@angular/core';
import {
  IonicPage,
  ToastController,
  AlertController,
  Content
} from 'ionic-angular';
import { Socket } from 'ng-socket-io';
import { Observable } from 'rxjs/Observable';
import { FormControl, FormBuilder } from '@angular/forms';

@IonicPage()
@Component({
  selector: 'page-chat-room',
  templateUrl: 'chat-room.html'
})
export class ChatRoomPage {
  @ViewChild(Content) content: Content;
  messages = [];
  nickname = '';

  messageForm: any;
  chatBox: any;

  constructor(
    private socket: Socket,
    private toastCtrl: ToastController,
    private alertCtrl: AlertController,
    public formBuilder: FormBuilder
  ) {
    this.messageForm = formBuilder.group({
      message: new FormControl('')
    });
    this.chatBox = '';

    this.presentPrompt().then((nickname: string) => {
      this.nickname = nickname;
      
      // connect to socket.io local server
      this.socket.connect();
      
      // send 'set-nickname' event to socket.io server
      this.socket.emit('set-nickname', this.nickname);

      this.getMessages().subscribe(message => {
        this.messages.push(message);
        this.scrollToBottom();
      });

      this.getUsers().subscribe(data => {
        let user = data['user'];
        if (data['event'] === 'left') {
          this.showToast('User left: ' + user);
        } else {
          this.showToast('User joined: ' + user);
        }
      });
    });
  }

  public sendMessage(message: string): void {
    if (!message || message === '') return;
    // send 'add-message' event to socket.io server
    this.socket.emit('add-message', { text: message });
    this.chatBox = '';
  }
  
  
  getMessages(): Observable<{}> {
    let observable = new Observable(observer => {
      this.socket.on('message', data => {
        // update observable when the 'message' event is sent from the server
        observer.next(data);
      });
    });
    return observable;
  }

  getUsers(): Observable<{}> {
    let observable = new Observable(observer => {
      // update observable when the 'users-changed' event is sent from the server
      this.socket.on('users-changed', data => {
        observer.next(data);
      });
    });
    return observable;
  }

  ionViewWillLeave(): void {
    this.socket.disconnect();
  }

  showToast(msg: string): void {
    let toast = this.toastCtrl.create({
      message: msg,
      duration: 2000
    });
    toast.present();
  }

  presentPrompt(): Promise<string> {
    return new Promise((resolve, reject) => {
      let alert = this.alertCtrl.create({
        title: 'Nickname',
        inputs: [
          {
            name: 'nickname',
            placeholder: 'doretta'
          }
        ],
        buttons: [
          {
            text: 'Random',
            role: 'cancel',
            handler: data => {
              resolve('random_' + Math.round(Math.random() * 100));
            }
          },
          {
            text: 'Go on',
            handler: data => {
              resolve(data.nickname);
            }
          }
        ]
      });
      alert.present();
    });
  }

  scrollToBottom() {
    setTimeout(() => {
      this.content.scrollToBottom();
    }, 100);
  }
}

Links