The error message you got was because you used the wrong url. The error message "Connection closed before receiving a handshake response" actually told you this (not receiving a response)
It should be 'ws://localhost:8080/socket.io/?EIO=3&transport=websocket'
Check here https://socket.io/docs/client-api/#With-custom-path & https://socket.io/docs/internals/ for details
e.g.
"EIO=3" # the current version of the Engine.IO protocol
BUT as other answer said and actually that was what socket.io readme said
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.
I have tested that to verify both ws & browser native Websocket can't not connect to a Socket.IO server. They are both immediately disconnected from Socket.IO server after they connects, with Socket.IO server send out the error message socket id xxx has disconnected with reason parse error
The ws author also mentioned here https://github.com/websockets/ws/issues/1390
using plain WebSocket to talk with a Socket.IO server does not work seamlessly.
If you want to use browser native Websocket you can use ws can server.