Node.js Server + Socket.IO + Android Mobile Applicatoin XHR Polling Error...?

28 Views Asked by At

I am building what I thought was a very simple server-client. I have built a simple Node.js server. Below is the implementation:

const http = require('http');
const { Server } = require('socket.io');

// Create an HTTP server
const server = http.createServer();

// Create a new instance of Socket.io by passing the HTTP server
const io = new Server(server);

// Listen for incoming socket connections
io.on('connection', (socket) => {
  console.log('A user connected');

  // Listen for messages from the client
  socket.on('message', (message) => {
    console.log('Message received:', message);

    // Broadcast the message to all connected clients
    io.emit('message', message);
  });

  // Listen for disconnection
  socket.on('disconnect', () => {
    console.log('A user disconnected');
  });
});

// Start the server and listen on port 3000
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server listening on http://192.168.0.191  port ${PORT}`);
});

I successfully validated this server by writing a Node.js client test script:

const io = require('socket.io-client');

// Replace 'http://localhost:3000' with the URL of your socket.io server
const socket = io('http://localhost:3000');

// Listen for 'connect' event
socket.on('connect', () => {
  console.log('Connected to server');

  // Read input from command line and send it to the server
  process.stdin.on('data', (data) => {
    const message = data.toString().trim();
    socket.emit('message', message);
    console.log("Next Message Sent:", message)
  });
});

// Listen for 'message' event
socket.on('message', (message) => {
  console.log('Received message from server:', message);
});

// Listen for 'disconnect' event
socket.on('disconnect', () => {
  console.log('Disconnected from server');
});

The problem that I'm having is with the Android application. I'm receiving XHR polling errors. I'm not able to pinpoint the root cause. This is the implementation:

build.gradle.kts:

implementation("dev.icerock.moko:socket-io:0.4.0")

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-feature android:name="android.software.leanback" android:required="true" />
    <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
    <uses-feature
        android:name="android.hardware.location.gps"
        android:required="false" />

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        ...
        android:usesCleartextTraffic="true"
        android:networkSecurityConfig="@xml/network_security_config"
        android:banner="@drawable/logo">
        ...

The implementation of the network-security-config.xml file:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">192.168.0.191</domain>
    </domain-config>
</network-security-config>

Finally, implementation relevant to the socket:

@Composable
    private fun createMokoSocketIO() {
        val coroutineScope = rememberCoroutineScope()

        coroutineScope.launch(Dispatchers.IO) {
            try {
                val socket = Socket(
                    endpoint = "http://192.168.0.10:3000",
                    config = SocketOptions(
                        queryParams = mapOf("payload" to "MyPayload"),
                        transport = SocketOptions.Transport.DEFAULT
                    )
                ) {
                    on(SocketEvent.Connect) {
                        println("moko.socket: connect")
                    }

                    on(SocketEvent.Connecting) {
                        println("moko.socket: connecting")
                    }

                    on(SocketEvent.Disconnect) {
                        println("moko.socket: disconnect")
                    }

                    on(SocketEvent.Error) {
                        println("moko.socket: error $it")
                    }

                    on(SocketEvent.Reconnect) {
                        println("moko.socket: reconnect")
                    }

                    on(SocketEvent.ReconnectAttempt) {
                        println("moko.socket: reconnect attempt $it")
                    }

                    on(SocketEvent.Ping) {
                        println("moko.socket: ping")
                    }

                    on(SocketEvent.Pong) {
                        println("moko.socket: pong")
                    }

                    on("message") { data ->
                        println("moko.socket: message=[$data]")
                        //...
                    }
                }
                socket.connect()
                socket.emit("message", "Hello, server!")
            } catch (e: URISyntaxException) {
                println("moko.socket: Error: ${e.message}")
                return@launch
            }
        }
    }

I need suggestions troubleshooting this issue. These are my logs:

moko.socket: error io.socket.engineio.client.EngineIOException: xhr poll error
moko.socket: error io.socket.client.SocketIOException: Connection error

I also tried to use a different 3rd party library, only to receive the same result:

implementation("io.socket:socket.io-client:1.0.0")

The server runs on my local machine, while the Android application is installed onto a mobile device. Both devices are on the same WIFI network. I even tried to connect using a tool such as NGROK, only to receive the same result.

Does anyone have any ideas? I hope all of this source code helps.

Thanks

1

There are 1 best solutions below

0
Coach Roebuck On

This video assisted me in finding a solution.

Both the server and the client required modifications.

This is the full implementation of the server:

    const http = require('http');
const express = require('express');
const { Server } = require('socket.io');
const app = express();

// Create an HTTP server
const server = http.createServer();

app.use(express.static("public"));

// Create a new instance of Socket.io by passing the HTTP server
const io = new Server(server);

// Listen for incoming socket connections
io.on('connection', (socket) => {
    let data = socket.handshake.query.payload;
    console.log(`A user connected. payload=[${data}]`);

  // Listen for messages from the client
  socket.on('message', (message) => {
    console.log('Message received:', message);

    // Broadcast the message to all connected clients
    io.emit('message', message);
  });

  // Listen for disconnection
  socket.on('disconnect', () => {
    console.log('A user disconnected');
  });
});

// Start the server and listen on port 3000
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server listening on http://192.168.0.191  port ${PORT}`);
});

On the Android app, I imported a different library. As of the time of this post, this is the latest version of the library:

implementation("io.socket:socket.io-client:2.1.0")

Finally, this is the full implementation, applicable to the socket:

@Composable
private fun createSocketIO() {
    val coroutineScope = rememberCoroutineScope()

    coroutineScope.launch(Dispatchers.IO) {
        try {
            val sc = SSLContext.getInstance("TLS")
            sc.init(null, arrayOf<TrustManager>(object : X509TrustManager {
                override fun checkClientTrusted(
                    chain: Array<X509Certificate?>?,
                    authType: String?
                ) {
                }

                override fun checkServerTrusted(
                    chain: Array<X509Certificate?>?,
                    authType: String?
                ) {
                }

                override fun getAcceptedIssuers(): Array<X509Certificate?> {
                    return arrayOfNulls(0)
                }
            }), SecureRandom())


            val builder = OkHttpClient.Builder()
                .hostnameVerifier { hostname: String?, session: SSLSession? -> true }
                .sslSocketFactory(sc.socketFactory, object : X509TrustManager {
                    override fun checkClientTrusted(
                        chain: Array<X509Certificate?>?,
                        authType: String?
                    ) {
                    }

                    override fun checkServerTrusted(
                        chain: Array<X509Certificate?>?,
                        authType: String?
                    ) {
                    }

                    override fun getAcceptedIssuers(): Array<X509Certificate?> {
                        return arrayOfNulls<X509Certificate>(0)
                    }
                })

            val okHttpClient = builder.build()


            IO.setDefaultOkHttpCallFactory(okHttpClient)
            IO.setDefaultOkHttpWebSocketFactory(okHttpClient)
            val opts: IO.Options = IO.Options()
            opts.secure = true
            opts.callFactory = okHttpClient
            opts.webSocketFactory = okHttpClient
            IO.socket(URI.create("http://192.168.0.10:3000?payload=348ritwnekrdgf43trkfn"), opts)?.let { socket ->
                [email protected] = socket

                socket.on("connecting") {
                    println("io.socket: Connecting from server")
                }

                socket.on(Socket.EVENT_CONNECT_ERROR) {
                    println("io.socket: Connection error")
                    it.map {
                        println(it)
                    }
                }

                socket.on("error") {
                    println("io.socket: Event Error")
                }

                socket.on(Socket.EVENT_CONNECT) {
                    println("io.socket: Connected to server")

                    socket.emit("message", "Hi! My name is Android!")
                }

                socket.on("message") { args ->
                    val message = args[0].toString()
                    println("io.socket: Received message from server: $message")
                }

                socket.on(Socket.EVENT_DISCONNECT) {
                    println("io.socket: Disconnected from server")
                }

                socket.connect()
            }
        } catch (e: URISyntaxException) {
            println("io.socket: Error: ${e.message}")
            return@launch
        }
    }
}