Socket.io

Socket.io is a library that allows a server to do server pull and server push. Server Push means the server can send something to the client without the client requesting it.

Socket.io relies on WebSockets for Web Applications.

It's useful to

  • πŸ’ Push notifications to the client
  • ✨ Push events to the client (instead of querying the API periodically for updates, just listen for new events)
  • 🍹 Making chat applications

Or more generally, making real-time applications. The server can push data to the client without being requested (server push).


Basic usage

Socket.io is quite easy to use.

  • Listen to an event: on("eventName", callback)
  • Stop listening to an event: off("eventName")
  • Stop listening to every event: off()
  • Emit an event: emit("eventName", JSON_DATA)
// ex: node client listening to "event"
client.on("event", (data) => {
    // send a reply
    client.emit("event-ack", data)

    // stop listening to event
    client.off("event")
})

Express Node.js server

This code targets node.js applications running Express.js. You may use Socket.io with other libraries such as Angular...

$ npm i socket.io

In a project generated with express-generator, append to bin/www

/**
* Create Socket.io listener
*/
app.io = require('socket.io')(server)
// you may remove this later
app.io.on('connection', () => {
    console.log('A new client connected to WebSockets')
})
CORS

See Handling CORS.

Ex: allowing any host to make requests.

- app.io = require('socket.io')(server)
+ app.io = require('socket.io')(server, {
+    cors: {
+        origin: "*"
+    }
+}

And in app.js, you must add a middleware

app.use((req,res,next) => {
    req.io = () => (app.io);
    next();
});

Then, in any request, you can fetch io for req.

router.post('/message', (req, res) => {
    req.io().emit('new-message', res.body)
});

There are many other alternatives, but I like this one because we are using a function, so we don't copy inside every request the io object, and we can use it everywhere from the req object.


Node.js client

You could create a socket.io client to test your code

$ npm i socket.io-client
const io = require("socket.io-client");
const client = io("http://localhost:3000");
client.on('connect', () => {
    console.log("Connected");
});

HTML client

You need to add the following script (version 4.5.3). See cdnjs.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.3/socket.io.js" integrity="sha512-iWPnCISAd/J+ZacwV2mbNLCaPGRrRo5OS81lKTVPtRg1wGTC20Cfmp5Us5RcbLv42QLdbAWl0MI57yox5VecQg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
const client = io("http://localhost:3000");
client.on('connect', () => {
    console.log("Connected");
});

Android client (Kotlin)

See Also

You must add the library in build.gradle

dependencies {
    ...
+    implementation ('io.socket:socket.io-client:2.0.0') {
+        exclude group: 'org.json', module: 'json'
+    }
    ...
}

Then, let's create a handler for our Socket.io connection.

package xxx

import io.socket.client.IO
import io.socket.client.Socket
import java.net.URISyntaxException


object SocketIOHandler {
    private lateinit var _socket: Socket
    val socket: Socket
        get() = _socket

    fun init() {
        try {
            _socket = IO.socket("http://10.0.2.2:3000")
            _socket.connect()
        } catch (_: URISyntaxException) {}
    }

    fun dispose() {
        _socket.disconnect()
    }
}

If you don't have one, you need to add a class Application. Inside, you will need to initialize and close socket.io client.

    <application
+        android:name=".MainApplication"
package xxx

import android.app.Application
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import xxx.SocketIOHandler

class MainApplication : Application(), DefaultLifecycleObserver {
override fun onCreate() {
        super<Application>.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this)
        SocketIOHandler.init()
    }

    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        SocketIOHandler.dispose()
    }
}

Then, you're now ready. For instance, in your MainActivity.

SocketIOHandler.socket.on("event") {
    // fetch argument (use JSONArray for [])
    val json = it[0] as JSONObject
    // fetch values { key: value }
    val value = json.getString("key")
    val value = json.getJSONObject("key")
    // ...
}

Additional notes

  • Execute code when connected
SocketIOHandler.socket.on(Socket.EVENT_CONNECT) {
    /* ... */
}