WebSocket Server

Teach you how to write a simple server:)

The Problem

  • In CSE115 you used HTTP request / responses to build web apps
  • If you wanted more data from the server after the page loads, you used AJAX
    • Server hosts JSON data at certain end points
    • Client makes an AJAX call to retrieve the most current data
  • But the server has to wait for a request before sending a response

  • What if the server wants to send time-sensitive data without waiting for a request?
  • In CSE115
    • Built a chat app using polling
    • Client sent AJAX requests at regular intervals
    • Only get updates when AJAX request is sent
  • Can use long-polling
    • Server hangs on poll requests until it has new data to send

  • A newer solution (Standardized in 2011)
  • Establishes a lasting connection
    • Enables 2-way communication between server and client
  • Server can push updates to clients over the web socket without waiting for the client to make a new request

socket.io

  • A library built on top of web sockets
  • Maintains connections and reconnecting
  • Uses message types
    • Similar to actors, except the message type is always a string
  • Add listeners to react to different message types
    • Receiving a message is an event
    • Listener code will be called when the event occurs

socket.io Server in Scala

  • New library
  • Link on the course website
  • Dependency included in pom.xml in examples repo

Web Socket Server

  • Import from the new library
  • Setup and start the server
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import com.corundumstudio.socketio.listener.{ConnectListener, DataListener, DisconnectListener}
    import com.corundumstudio.socketio.{AckRequest, Configuration, SocketIOClient, SocketIOServer}

    class Server() {

    val config: Configuration = new Configuration {
    setHostname("localhost")
    setPort(8080)
    }

    val server: SocketIOServer = new SocketIOServer(config)

    server.addConnectListener(new ConnectionListener())
    server.addDisconnectListener(new DisconnectionListener())
    server.addEventListener("chat_message", classOf[String], new MessageListener())
    server.addEventListener("stop_server", classOf[Nothing], new StopListener(this))

    server.start()
    }

  • Create a configuration object for the server
  • This server will run on localhost port 8080
    1
    2
    3
    4
    val config: Configuration = new Configuration {
    setHostname("localhost")
    setPort(8080)
    }

  • Create and start the server
  • Use the configuration to tell the library how to setup the server
  • Call the start() method to start listening for connections
    1
    2
    3
    4
    5
    val server: SocketIOServer = new SocketIOServer(config) // Create a server

    ...

    server.start() // Start the server

  • Add listener to handle different event types
  • Connect and disconnect listeners to react to clients connecting and disconnecting
  • Event listeners for each different message tyoe received from clients
    1
    2
    3
    4
    server.addConnectListener(new ConnectionListener()) // added connect listener
    server.addDisconnectListener(new DisconnectionListener()) // added disconnect listener
    server.addEventListener("chat_message", classOf[String], new MessageListener()) // added event listener
    server.addEventListener("stop_server", classOf[Nothing], new StopListener(this)) // added event listener

  • For connect and disconnect
    • Create classes overriding ConnectListener and DisconnectListener
    • Implement the onConnect / onDisconnect methods
  • These methods take a reference to the sending socket as a parameter
    • Can use this reference to send messages to the client
    • Usually want to store each reference to send messages later
      1
      2
      server.addConnectListener(new ConnectionListener())
      server.addConnectListener(new DisconnectionListener())
      1
      2
      3
      4
      5
      class ConnectionListener() extends ConnectListener {
      override def onConnect(socket: SocketIOClient): Unit = {
      println("Connected: " + socket)
      }
      }
      1
      2
      3
      4
      5
      class DisconnectionListener() extends DisconnectListener {
      override def onDisconnect(socket: SocketIOClient): Unit = {
      println("Disconnected: " + socket)
      }
      }

  • To receive messages, specify the message type and the class of the message
    • Create classes extending DataListener[message_type]
  • For message class we’ll use
    • String to receive text data
    • Nothing if it’s just a message (Similar to an actor receiving a case object)
      1
      2
      server.addEventListener("chat_message", classOf[String], new MessageListener())
      server.addEventListener("stop_server", classOf[Nothing], new StopListener(this))
      1
      2
      3
      4
      5
      6
      class MessageListener() extends DataListener[String] {
      override def onData(socket: SocketIOClient, data: String, ackRequest: AckRequest): Unit = {
      println("received_message: " + data + "from: " + socket)
      socket.sendEvent("ACK", "I received your message of " + data)
      }
      }
      1
      2
      3
      4
      5
      6
      7
      class StopListener(server: Server) extends DataListener[Nothing] {
      override def onData(socket: SocketIOClient, data: Nothing, ackRequest: AckRequest): Unit = {
      println("stopping server")
      server.server.stop()
      println("safe to stop program")
      }
      }

  • Use the reference to the Socket to send messages to the client
  • Specify the type of the message as a String
  • If the message contains data, use a second String
    1
    socket.sendEvent("ACK", "I received your message of " + data)

Web Socket Clients

  • We’ve set up a web socket server that will listen for connections and process message
  • Now, let’s build a web socket client that will connect to the server

WebSocket Client - Web

  • First, setup the HTML
  • Layout and style of the page
    • Could add CSS for more style
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      <!DOCTYPE html>
      <html lang="en">
      <head>
      <meta charset="UTF-8">
      <title>Web Socket Client Example</title>
      <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
      </head>
      <body>
      <input type="text" id="chat_input"/>
      <button id="gold" onclick="sendMessage();">Submit</botton>

      <div id="display_message"></div>

      <script src="WebClient.js"></script>

      </body>
      </html>

  • Download the socket.io JavaScript client library
  • This library contains all the code we’ll need to connect to pur server
    1
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>

  • Add elements for the user to enter and send a message
  • In JavaScript, we’ll implement the sendMessage() function
    1
    2
    3
    4
    <input type="text" id="chat_input"/>
    <button id="gold" onclick="sendMessage();">Submit</botton>

    <div id="display_message"></div>

  • Download our JavaScript file
  • This script runs codes to connect to the server as soon as it’s downloaded
    • Include this at the end of the body so the page loads before connecting to the server
      1
      <script src="WebClient.js"></script>

  • In WebClinet.js
  • Call io.connect to the server
    • Returns a reference to the created socket
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      const socket = io.connect("http://localhost:8080", {transports: {'websocket'}});

      socket.on('ACK', function (event) {
      document.getElementById("display_message").innerHTML = event;
      });

      socker.on('server_stopped', function (event) {
      document.getElementById("display_message").innerHTML = "The server has stopped";
      });

      function sendMessage() {
      let message = document.getElementById("chat_input").value
      document.getElementById("chat_input").value = "";
      socket.emit("chat_message", message);
      }

  • Define how the socket will react to different message types with the “on” method
  • The “on” method takes the message type and a function as arguments
    • Call the function whenever a message of that type is received from the server
      1
      2
      3
      4
      5
      6
      7
      socket.on('ACK', function (event) {
      document.getElementById("display_message").innerHTML = event;
      });

      socker.on('server_stopped', function (event) {
      document.getElementById("display_message").innerHTML = "The server has stopped";
      });

  • The function should take a parameter which will contain the data of the message if there is any
  • We receive an ACK message containing a string which we display on the page (Similar to case class)
  • We receive a server_stopped message and inform he user that the server stopped (Similar to case object)

  • To send a message, call emit
  • Takes the message type and the content of the message, if any
  • Can call emit with only message type to send a message with no content (Similar to case object)

Web Socket Demo


Lecture Question

Task:
Write a Web Socket Server that counts the number of messages it receives


In a package named server, write a class named LectureServer that:

  • When created, sets up a web socket server listening for connections on localhost:8080
  • Listens for messages of type “increment” with no data
  • Has a method named numberOfMessages that returns (as an Int) the number of times a message of type “increment” was received

Testing:
No test:)

Hint:
If you have no idea how to write, check prof. example repo -> link