Closed iKK001 closed 2 years ago
I'm sorry I don't understand the reason and won't spend time to deal with your unusual use-case, even if this is technically feasible.
Please read Socket.io room-support
Note: Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server
Good Luck,
I completely understand that Socket.IO is not a Websocket implementation.
However, your library supports Socket.IO (at least with one concrete example in your library) - therefore I was hoping that you support entire Socket.IO capability (including "websocket only" mode).
If you look closer to the Socket.IO documentation, Socket.IO is capable to connect in a mode called "websocket only" (as opposed to "polling").
And this "websocket only" mode of Socket.IO is obviously badly supported/documented with your Arduino library.
The reasons we want the "websocket only" mode are: --> being able to get a speedy connectivity without the need of a load-balancer (our application is hosted on Google Cloud Run and we use Socket.IO to exchange fast messages. We have iOS, Android and Web running with this "websocket only" mode - no issues. And we would like to achieve the same now with Arduino.)
Due to scallablility and the limited number of clients of one Socket.IO node, the Cloud Run Server creates new nodes whenever needed. And those nodes are slow if used with "polling". Therefore the need of a "websocket only" mode. If Arduino can't, we are oblidged to use an expensive load balancer. And we would not like that really.
I'll create a new version for you to test, using transport=websocket
instead of transport=polling
.
Will let you know when it's ready by tomorrow
Hi @iKK001
The WebSockets_Generic Releases v2.11.1 has just been published to to add support to websocket only mode for Socket.IO (sticky-session
mode)
Your enhancement request, leading to new v2.11.1, has also been noted in Contributions and Thanks.
You have to use WebSocketClient_Sticky_SocketIO_NINA instead of WebSocketClientSocketIO_NINA, with the new selection
// For transport=websocket instead of transport=polling
#define USING_STICKY_SESSION_SIO true
See the new handshake now is Handshake:GET /socket.io/?EIO=4&transport=websocket HTTP/1.1
Start Portenta_H7_WebSocketClient_Sticky_SocketIO on PORTENTA_H7_M7 with Ethernet using Portenta_Ethernet Library
WebSockets_Generic v2.11.1
WebSockets Client @ IP address: 192.168.2.133
Connecting to WebSockets Server @ IP address: 192.168.2.30, port: 8080
[WS] WebSockets_Generic v2.11.1
[WS] [wsIOc] found EIO=4 disable EIO ping on client
[WS] [WS-Client][connectedCb] Connected to Host:192.168.2.30, Port:8080
[WS] [WS-Client] [sendHeader] Sending header...
[WS] sendHeader: client->cKey = Kp/b0RFGMw/WSNGn35/s3A==
[WS] [WS-Client] [sendHeader] Handshake:GET /socket.io/?EIO=4&transport=websocket HTTP/1.1 <===== new mode
Host: 192.168.2.30:8080
Connection: keep-alive
Authorization: 1234567890
User-Agent: arduino-WebSocket-Client
Please test and post any new issue of this new release. Thanks.
Best Regards,
transport=websocket
with sticky-session SIO server. Check Run websocket only mode for Socket.IO #16Thank you very much for this kind extension of the library. I appreciate it.
Using your new "sticky-session mode" library update, I observed that the initial connection starts with a "https" connection.
However, Socket.IO in a "sticky-session only mode" must work with "wss" protocol.
I suscpect that you tested the Arduino library change on a http-server instead of a socket.io-server.
Below a working example established on a Web-client (not Arduino) with just "websocket only transport". As you can see in the Request-Example shows clearly only "ws" (instead of "http").
Working Example (Web-client):
Because you didn't specify how your server is using, the code WebSocketClient_Sticky_SocketIO_NINA example was tested with this Multi_Sticky_SIO server as follows:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
const process = require('process');
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8080);
console.log(`Worker ${process.pid} started on port 8080`);
}
If you're using different server mode, such as specified in Using multiple nodes with nodejs-cluster, you can just use the normal WebSocketClientSocketIO_NINA example
const cluster = require("cluster");
const http = require("http");
const { Server } = require("socket.io");
const numCPUs = require("os").cpus().length;
const { setupMaster, setupWorker } = require("@socket.io/sticky");
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
const httpServer = http.createServer();
// setup sticky sessions
setupMaster(httpServer, {
loadBalancingMethod: "least-connection",
});
// setup connections between the workers
setupPrimary();
// needed for packets containing buffers (you can ignore it if you only send plaintext objects)
// Node.js < 16.0.0
cluster.setupMaster({
serialization: "advanced",
});
// Node.js > 16.0.0
// cluster.setupPrimary({
// serialization: "advanced",
// });
//httpServer.listen(3000);
httpServer.listen(8080);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on("exit", (worker) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
} else {
console.log(`Worker ${process.pid} started on 8080 port`);
const httpServer = http.createServer();
const io = new Server(httpServer);
// use the cluster adapter
io.adapter(createAdapter());
// setup connection with the primary process
setupWorker(io);
io.on("connection", (socket) => {
/* ... */
});
}
I think the transport mode will be automatically changed to during the negotiation from
Handshake:GET /socket.io/?EIO=4&transport=polling HTTP/1.1
to
Handshake:GET /socket.io/?EIO=4&transport=websocket&sid=AZdu9h8dZ3ZOqgiaAAAA HTTP/1.1
Start ESP8266_WebSocketClientSocketIO on ESP8266_NODEMCU_ESP12E
WebSockets_Generic v2.11.1
WebSockets Client started @ IP address: 192.168.2.132
Connecting to WebSockets Server @ IP address: 192.168.2.30, port: 8080
[WS] [WS-Client] beginSocketIO with IPAddress
[WS] [WS-Client] beginSocketIO with const char
[WS] WebSockets_Generic v2.11.1
[WS] [wsIOc] found EIO=4 disable EIO ping on client
[WS] [WS-Client] Connect ws...
[WS] [WS-Client] connectedCb
[WS] [WS-Client][connectedCb] Connected to Host:192.168.2.30, Port:8080
[WS] [WS-Client] [sendHeader] Sending header...
[WS] sendHeader: client->cKey = Rw3kPWMBT0VXNqhWW0y/xA==
[WS] [WS-Client] [sendHeader] Handshake:GET /socket.io/?EIO=4&transport=polling HTTP/1.1 <=== orig. Handshake
Host: 192.168.2.30:8080
Connection: keep-alive
Authorization: 1234567890
User-Agent: arduino-WebSocket-Client
[WS] [write] n:166, t:10104
[WS] [write] Write, Length :166, Left :0
[WS] [WS-Client] [sendHeader] Sending header... Done (us):29229
[WS] [WS-Client][handleHeader] RX:HTTP/1.1 200 OK
[WS] [WS-Client][handleHeader] RX:Content-Type: text/plain; charset=UTF-8
[WS] [WS-Client][handleHeader] RX:Content-Length: 97
[WS] [WS-Client][handleHeader] RX:Date: Tue, 14 Dec 2021 00:51:13 GMT
[WS] [WS-Client][handleHeader] RX:Connection: keep-alive
[WS] [WS-Client][handleHeader] RX:Keep-Alive: timeout=5
[WS] [WS-Client][handleHeader] Header read fin.
[WS] [WS-Client][handleHeader] Client settings:
[WS] [WS-Client][handleHeader] - cURL:/socket.io/?EIO=4
[WS] [WS-Client][handleHeader] - cKey:Rw3kPWMBT0VXNqhWW0y/xA==
[WS] [WS-Client][handleHeader] Server header:
[WS] [WS-Client][handleHeader] - cCode:200
[WS] [WS-Client][handleHeader] - cIsUpgrade:0
[WS] [WS-Client][handleHeader] - cIsWebsocket:1
[WS] [WS-Client][handleHeader] - cAccept:
[WS] [WS-Client][handleHeader] - cProtocol:arduino
[WS] [WS-Client][handleHeader] - cExtensions:
[WS] [WS-Client][handleHeader] - cVersion:0
[WS] [WS-Client][handleHeader] - cSessionId:
[WS] [WS-Client][handleHeader] Still missing cSessionId try Socket.IO
[WS] [WS-Client][handleHeader] socket.io json: 0{"sid":"AZdu9h8dZ3ZOqgiaAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000}
[WS] [WS-Client][handleHeader] - cSessionId: AZdu9h8dZ3ZOqgiaAAAA
[WS] [WS-Client][handleHeader] Header read fin.
[WS] [WS-Client][handleHeader] Client settings:
[WS] [WS-Client][handleHeader] - cURL:/socket.io/?EIO=4
[WS] [WS-Client][handleHeader] - cKey:Rw3kPWMBT0VXNqhWW0y/xA==
[WS] [WS-Client][handleHeader] Server header:
[WS] [WS-Client][handleHeader] - cCode:200
[WS] [WS-Client][handleHeader] - cIsUpgrade:0
[WS] [WS-Client][handleHeader] - cIsWebsocket:1
[WS] [WS-Client][handleHeader] - cAccept:
[WS] [WS-Client][handleHeader] - cProtocol:arduino
[WS] [WS-Client][handleHeader] - cExtensions:
[WS] [WS-Client][handleHeader] - cVersion:0
[WS] [WS-Client][handleHeader] - cSessionId:AZdu9h8dZ3ZOqgiaAAAA
[WS] [WS-Client][handleHeader] found cSessionId
[WS] [WS-Client] [sendHeader] Sending header...
[WS] sendHeader: client->cKey = /TMYxAAoTpVCfzn3Ic/zsA==
[WS] [WS-Client] [sendHeader] Handshake:GET /socket.io/?EIO=4&transport=websocket&sid=AZdu9h8dZ3ZOqgiaAAAA HTTP/1.1 <============== Handshake updated here
Host: 192.168.2.30:8080
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: /TMYxAAoTpVCfzn3Ic/zsA==
Sec-WebSocket-Protocol: arduino
Authorization: 1234567890
User-Agent: arduino-WebSocket-Client
I'm sorry I don't have more time to help here.
I suggest if you have more issues, test on ESP32/ESP8266, then post the issue in arduinoWebSockets library issues for help.
Good Luck,
I understand that it was not clear how I set up the server.
I also understand that it takes a bit of a moment to understand the two major ways you can run a multi-node Socket.IO configuration. In my case, there are several server nodes running but without "sticky-session" and without http-protocol.
The way I run the multiple Server-nodes is without the need of an expensive load-balancer. (i.e. only with "websocket" and NO long-polling http). (as described here under Remarks point 2) - it says:
the WebSocket transport does not have this limitation, since it relies on a single TCP connection for the whole session. Which means that if you disable the HTTP long-polling transport (which is a perfectly valid choice in 2021), you won't need sticky sessions:
Therefore I am not running the server-nodes with "sticky sessions" but instead with "websocket only".
You can achieve that by instantiating one server-node with the necessary Server-Option as follows:
const io = new Server(httpServer, {
transports: ["websocket"],
});
(Instead of what you did: const io = new Server(httpServer);
)
The corresponding Web-Client option is described here.
So it is very simple for you to test:
Simply add the Server-Option transports: ["websocekt"],
to your server and you are good to go !
All we need is a server that only relies on "websocket" (and no longer uses any kind of http).
With such a "websocket-only"-Server, you can then hopefully see what to improve in your client-library. The important thing is that your library only uses "wss://" protocol (or "ws://" for local server) at any time (i.e. no longer "http" and upgrade to "ws" as in default mode).
Please give it a chance. Thank you very much.
That websocket-only
-Server is not suggested, per Socket.IO Upgrade mechanism section
By default, the client establishes the connection with the HTTP long-polling transport.
But, why?
While WebSocket is clearly the best way to establish a bidirectional communication, experience has shown that it is not always possible to establish a WebSocket connection, due to corporate proxies, personal firewall, antivirus software...
From the user perspective, an unsuccessful WebSocket connection can translate in up to at least 10 seconds of waiting for the realtime application to begin exchanging data. This perceptively hurts user experience.
To summarize, Engine.IO focuses on reliability and user experience first, marginal potential UX improvements and increased server performance second.
Is there a way to run "websocket" only mode with Socket.IO on this Arduino library ?
I made a local Socket.IO client that works well with the WebSockets_Generic Example "WiFiNINA-->WebSocketClientSocketIO_NINA" - but only if the server has "transport=["polling", "websocket"]," defined.
If I change the server to "transport=["websocket"]," then it does not work anymore.
However, our application absolutely requires "websocket only" configuration !
On other Socket.IO clients there is a possibility to force to "websocket only". For example in the iOS-client there is a config setting called ".forceWebsockets(true)," that makes exactly that.
The keyword, I think, is called "websocket only". Please refer to the Socket.IO documentation here and the error you get if websocket only is missing is explained here
Is there a "websocket only" mode for the WebSockets_Generic library ? If yes, how would I configure it ?