Problems with a websocket server

Hi, I am pretty new to networking.
Recently i switched from ngrok because of low bandwith cap.
But, the websocket that I was running in node worked well when I opened a port 3000 to public with ngrok. But with zrok it doesnt seem to work... any ideas?

Hi @magzzy124! Welcome to the forum and the community. I just checked. WebSockets do work with zrok public shares, but you must ensure the WebSocket is available on the same TCP port as the web server.

Will you share some of the specifics about the steps you took? Maybe I can spot the part that's not lining up perfectly.


Diagnosing the problem: You'll find the WebSocket upgrade request in your browser's developer tools under a heading like "network requests." That will be a GET request for the WebSocket path, which is often /ws. The web server should respond with a 101 status (switching protocols). At that point the Javascript client part of the web application should initiate a WebSocket connection to the same server TCP port. The connection will reuse the TCP stream and you should see activity in the browser's developer tools Javascript console.

Thanks for the reply!
So here is some more info, so after I downloaded your cli, I ran "zrok enable" with my id, and then I immediately ran "zrok share public 3000" (as the websocket server running in express works on port 3000). And first of, I tested the ip address in insomnia(similar to postman) using their wss request connection which worked fine, but in chrome it doesn't work. Also when I tried using online websocket testers it still didn't work.
But with ngrok and also with localtunnel it works just fine in both chrome, insomnia and online testers.
Thanks in advance!

Edit... Just wanted to add that normal get and post requests do work just fine!

Does my WebSocket test server work for you?

https://mij0ed3quq9t.share.zrok.io/

Hmm, doesn't seem to work.. Also the online test confirms the same.
I use this tester: Online Websockets Tester - Debug Client Tool

I tested the ip address in insomnia(similar to postman) using their wss request connection which worked fine

Did this test involve sending a request in Insomnia to a WebSocket URL like ws://1.2.34? If so, what is the server listening at that IP address, a test service, zrok, your Express app, or something else?

Also when I tried using online websocket testers it still didn't work.

Did this test involve typing some WebSocket URL in the testing tool like ws://1.2.3.4? If so, which URL were you testing, the zrok public share URL or something else?

Hmm, doesn't seem to work..

What happens when you visit my WebSocket test share?

Yes it did, and it looks like this.


I also looks the same when using the online tester.
But this is interesting, now I see what is on your link. Now I also realize that for some reason your websocket url is actually wss://mij0ed3quq9t.share.zrok.io/ws, it adds ws at the end, let me test this and I'll get back to you as soon as I do!

Does my WebSocket test server work when you visit in a regular web browser? I think the problem is you're initiating WebSocket directly, but zrok public shares are HTTP, so you must use an "upgrade" HTTP request to obtain a WebSocket.

Oh, and by "does it work" I meant does my test app show server heartbeats received?

I think you are absolutely right!
Your websocket server does work, with the hearts emojies and everything.

1 Like

But I am not sure how to proceed..

If you're using a web framework there's probably a method for serving WebSockets on the same TCP port as HTTP. If so, that's what you want.

I am using react native for my client, and express.js and a WebSocket js constructor. Could you guide me in the right direction please? Thanks!

1 Like

This seemed to work. I rewrote my Python Flask WS heartbeat server. Well, Claude rewrote it. :wink:

const express = require('express');
const { WebSocketServer } = require('ws');
const http = require('http');
const path = require('path');

const app = express();
const server = http.createServer(app);
const wss = new WebSocketServer({ server });

// Serve static files from 'templates' directory
app.use(express.static(path.join(__dirname, 'templates')));

// Serve index.html for root route
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'templates', 'index.html'));
});

// Array of heart emojis
const HEARTS = ['โค๏ธ', '๐Ÿ’–', '๐Ÿ’', '๐Ÿ’—', '๐Ÿ’“', '๐Ÿ’•', '๐Ÿ’ž', '๐Ÿ’˜', '๐Ÿ’Ÿ'];

// Handle WebSocket connections
wss.on('connection', (ws) => {
    console.log('Client connected');
    
    let heartbeatInterval;
    let lastHeartbeat = 0;
    const HEARTBEAT_INTERVAL = 10000; // 10 seconds
    
    // Start sending heartbeats
    heartbeatInterval = setInterval(() => {
        const currentTime = Date.now();
        if (currentTime - lastHeartbeat >= HEARTBEAT_INTERVAL) {
            const heart = HEARTS[Math.floor(Math.random() * HEARTS.length)];
            const timestamp = new Date().toLocaleString('en-US', {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit',
                hour12: false
            });
            
            ws.send(`${heart} ${timestamp}`);
            lastHeartbeat = currentTime;
        }
    }, 1000);
    
    // Handle client disconnection
    ws.on('close', () => {
        console.log('Client disconnected');
        clearInterval(heartbeatInterval);
    });
    
    // Handle connection errors
    ws.on('error', console.error);
});

// Start the server
const PORT = process.env.PORT || 9876;
server.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});

Here's the rest of the demo materials I put together today: GitHub - openziti-test-kitchen/websocketz

1 Like

Ok, I managed to replicate this. And it does work if i put my testing html file in the templates folder.
But how would I manage this if I am, for example, running this server on port 3000, but I am building a react mobile application on port 8080. So basically, i can't statically serve the html..
Sorry if I am missing something obvious!

The main requirement here is the HTTP and WebSocket servers are listening on the same TCP port. zrok is sharing a single port and introducing the WebSocket via HTTP. The WebSocket connection literally re-uses the same TCP stream of packets that was created with HTTP.

No need for static HTML. I added a React client example here to represent what your mobile app needs to do to obtain a WebSocket via an HTTP zrok public share: websocketz/react-client at main ยท openziti-test-kitchen/websocketz ยท GitHub

1 Like

Ok, thanks for the help!
I will play around a bit and I'll see what I can do.

You're welcome. :slightly_smiling_face:

The React example assumes that the WebSocket URL has the same domain name and port as the HTTP URL. Then it initiates the connection with the standard WebSocket interface. This works with zrok because the TCP stream already exists from the preceding HTTP response.

ws.current = new WebSocket(wsUrl)

from example L92: websocketz/react-client/src/App.jsx at main ยท openziti-test-kitchen/websocketz ยท GitHub