I'm trying to connect a python3 socket with a JavaScript WebSocket. I'm testing the server code on localhost:8080, and connecting with Chrome(94.0).
The WebSocket client and the socket server do establish a connection. However, the connection would be shut down if the server did not follow the protocol and respond to the handshake. According to the protocol, the client WebSocket sends a utf-8 encoded GET request to the socket server, and the server must parse that request in order to complete the handshake.
What I get from the client is this:
\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03\x97t\xb1\xf7\xac\x99\x91\xd4\xf5\n\xdf\xc3X\x8af<\xbe\x99(0\x88\x9a!\xc6\xc9\x17]\xfe\xd9sP\xcc \xa8\xcf\x90{\x8a\xfcm\xbcj-5\xdf\xf90\x81\xc8Y\xc1\x85q"\xfe!C\xbb\t\xbd}\xe0\x8d\xf1\xe5\x00 \x1a\x1a\x13\x01\x13\x02\x13\x03\xc0+\xc0/\xc0,\xc00\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00/\x005\x01\x00\x01\x93\x1a\x1a\x00\x00\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\n\x00\n\x00\x08\xfa\xfa\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00#\x00\x00\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x12\x00\x10\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x003\x00+\x00)\xfa\xfa\x00\x01\x00\x00\x1d\x00 \xfa)\xd5\x85<\x81.\x7f\xc0\x87wA!\xf1\xc9\xf80\xeb\x01\xdc\xed3m\xd1\x98\xd6\xd8\xd7\x7f#\rM\x00-\x00\x02\x01\x01\x00+\x00\x0b\n\x8a\x8a\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02JJ\x00\x01\x00\x00\x15\x00\xf7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
The client consistently sends these bytes, although it this does not look like a legitimate GET request. This can't be decoded by utf-8 or by other encodings including utf-16-le, utf-16-be, unicode-escape, etc.
client source:
var client = new WebSocket('wss://127.0.0.1:8080')
client.onopen = function(e) {
console.log("open");
}
client.onmessage = function(e) {
console.log(e.data)
}
server handshake handler:
def handshake(client, address):
req = client.recv(1024).decode('utf-8')
key = (re.search('Sec-WebSocket-Key:\s+(.*?)[\n\r]+', req).groups()[0].strip())
response_key = b64encode(sha1(key + sockey).digest())
response = '\r\n'.join(websock_ans).format(key=response_key)
client.send(response.encode('utf-8'))
As the client request isn't utf-8 bytes, this gives a decoding error in line 2.
following the websocket protocol (https://datatracker.ietf.org/doc/html/rfc6455), sockey and websock_ans are defined as follows:
sockey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
websock_ans = {
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: {key}\r\n\r\n',
}
Does WebSocket compress/encrypt its GET requests? As far as I checked, the documents do not state any compression/encryption algorithms.
Thanks in advance.
Use gzip to convert the bytes into objects.
Related
About 4 hours of research...here we go.
I have a C# program that sends and listens for anything coming in a specific Socket. Using the sockets, C# can send stuff to it and can receive from it just fine. Now, going to my JavaScript file, I'm using the WebSocket interface to communicate with C#, but doesn't work (usually times out after a couple of minutes). When the Socket is online, the JavaScript code will take up to about 4 minutes then throw an error saying "WebSocket opening handshake timed out". The thing is I know that it can find because, when the port of the ip doesn't exist the JavaScript file throws an error in the next couple seconds.
Things I've done:
Turn off all firewalls, use both ws and wss at the beginning of the ip and port (ex: wss://xxx.xxx.x.xx:11111), change the port, change the ip to a valid ip still reachable, research for 4 hours.
C#:
IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddr = IPAddress.Parse("ip");
IPEndPoint localEndPoint = new IPEndPoint(ipAddr, 11111);
Socket listener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(10);
while (true)
{
Console.WriteLine("Waiting connection...");
Socket clientSocket = listener.Accept();
byte[] bytes = new Byte[1024];
string data = null;
while (true)
{
int numByte = clientSocket.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, numByte);
if (data.IndexOf("<EOF>") > -1)
{
break;
}
}
Console.WriteLine("Text received -> {0} ", data);
byte[] message = Encoding.ASCII.GetBytes("Test Server");
clientSocket.Send(message);
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
JavaScript:
socket = new WebSocket("wss://ip:11111");
socket.onopen = function()
{
alert("Connected!");
}
socket.onerror = function()
{
alert("Connection Failed");
}
The ip is local
Long story short, C# can communicate with itself and JavaScript can find it but can't communicate with it.
Properly complete a handshake. (Or use a library / connection type that does.)
The WebSocket protocol (as original defined in RFC6455 - The WebSocket Protocol) does not open a plain unrestricted socket, in part for security reasons.
Since the handshake is not complete, the client WS request will timeout as the HTTP “Upgrade” response is never received. Until the handshake is complete, the WS will not be active over the underlying TCP connection.
Initiating a WebSocket connection (“the handshake”) is defined in section 4 of the RFC. It is also discussed in How JavaScript works: Deep dive into WebSockets and HTTP/2 with SSE + how to pick the right path.
The client establishes a WebSocket connection through a process known as the WebSocket handshake. This process starts with the client sending a regular HTTP request to the server. An Upgrade header is included in this request which informs the server that the client wishes to establish a WebSocket connection.
..
Now that [after] the handshake is complete the initial HTTP connection is replaced by a WebSocket connection that uses the same underlying TCP/IP connection. At this point, either party can start sending data.
I am building a system using an ESP8266/NodeMcu module (similar to an Arduino, just with networking capabilities) and a NodeJs server running on the local network.
To discover the IP address of the server, I'm trying to use UDP broadcasting on the NodeMcu module. The idea is to send out a message on the local broadcasting IP (e.g. 192.168.1.255). The server then receives the message and sends a response, confirming that it is the server. This way, the NodeMcu knows the direct address of the server for further communication.
The problem is, that the server is completely flooding itself with the same message whenever it receives the first message from the NodeMcu, while the NodeMcu actually sends out a message only once a second.
It looks like this on the NodeMcu side:
[UDP] Sending UDP Broadcast on IP: 192.168.43.255, Port: 8080, Message: ESP8266 UDP Server Discovery Broadcast
The server outputs something like this, many times a second:
[10:33:07] 127.0.0.1:8080 # service discovery : ESP8266 UDP Server Discovery Broadcast
[10:33:07] 127.0.0.1:8080 # service discovery : ESP8266 UDP Server Discovery Broadcast
[10:33:07] 127.0.0.1:8080 # service discovery : ESP8266 UDP Server Discovery Broadcast
It doesn't make sense that it's receiving that many messages, especially because it's apparently coming from 127.0.0.1 and not the IP of the NodeMcu. It also doesn't send out any response.
I tried to receive the broadcast on my phone with a UDP Monitor app, an application called Packet Sender and the Linux Terminal. It all worked fine, and sending a manual response triggered the acknowledgement on the NodeMcu.
So I'm thinking there has to be some kind of error with the server, or with the network I'm using. The server is running on Linux on my computer, while I'm hosting the network via a hotspot on my phone (my real WiFi network blocked UDP broadcasting). The Linux firewall is turned off.
I'm no expert in JavaScript or NodeJs by any means and the server was written by someone I'm working with, but he has no clue either. Anyway, this is the important part on the server:
client.on('listening', function () {
var address = client.address();
debugMessage(
format('Service discovery running on port %s', config.port)
);
client.setBroadcast(true);
});
client.on('message', function (message, rinfo) {
debugMessage(
format('%s:%s # service discovery : %s', rinfo.address, rinfo.port, message)
);
client.send(message, 0, message.length, rinfo.port, rinfo.ip);
});
client.bind(config.port);
The code on the NodeMcu looks like this:
#include <ESP8266WiFi.h> // WiFi library
#include <WiFiUdp.h> // UPD functionality
// UDP variables
WiFiUDP udp;
unsigned int localUdpPort = 8080;
char incomingPacket[255];
const char broadcastMessage[] = "ESP8266 UDP Server Discovery Broadcast";
// Server details - written to when the server is found
IPAddress serverIp = ~WiFi.subnetMask() | WiFi.gatewayIP(); // Use Broadcast Address as default in case the UDP service discovery isn't working as intended
unsigned int serverPort = localUdpPort; // Use local port as default in case the UDP service discovery ins't working as intended
void setupWiFi()
{
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
#if LOGGING
Serial.println("Connecting to network: " + (String) WIFI_SSID);
#endif
while (WiFi.status() != WL_CONNECTED)
{
delay(100);
}
#if LOGGING
Serial.print("Connected to network, Local IP Address: ");
Serial.println(WiFi.localIP());
#endif
udp.begin(localUdpPort); // begin listening on UDP port
#if LOGGING
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), localUdpPort);
#endif LOGGING
}
// Discover the server via a UDP broadcast, and store it's IP and Port in the local network in field variables for later use
// IMPORTANT - For the server to work, the Linux Firewall has to be disabled!!!
void discoverServer()
{
changeColor(PURPLE, false); // change the color of the RGB status LED to signal that the program is searching for the server
bool serverFound = false; // stop when the server is found
IPAddress broadcastIp = ~WiFi.subnetMask() | WiFi.gatewayIP(); // Get the Broadcast IP of the local network (e.g. 192.168.0.255)
while (!serverFound)
{
// Send UDP Broadcast
udp.beginPacket(broadcastIp, localUdpPort);
udp.write(broadcastMessage);
udp.endPacket();
#if LOGGING
Serial.printf("[UDP] Sending UDP Broadcast on IP: %s, Port: %d, Message: %s\n", broadcastIp.toString().c_str(), localUdpPort, broadcastMessage);
#endif
delay(1000); // Pause a few milliseconds to avoid flooding the network
// Receive UDP packets
int packetSize = udp.parsePacket();
if (packetSize > 0)
{
// Read incoming UDP Packet
int len = udp.read(incomingPacket, 255);
if (len > 0)
{
incomingPacket[len] = 0;
}
#if LOGGING
Serial.printf("[UDP] Received %d bytes from %s, port %d\n", packetSize, udp.remoteIP().toString().c_str(), udp.remotePort());
Serial.printf("[UDP] Packet contents: %s\n", incomingPacket);
#endif
// Check if the received message is from the server we are searching for
if (strcmp(incomingPacket, broadcastMessage) == 0)
{
serverIp = udp.remoteIP();
serverPort = udp.remotePort();
#if LOGGING
Serial.printf("[UDP] Found Server on IP: %s, Port: %d\n", serverIp.toString().c_str(), serverPort);
#endif
serverFound = true;
changeColor(YELLOW, false); // Change status color of RGB LED back to yellow
}
}
}
}
I'm really wondering if there is something wrong with the server, the network or the NodeMcu. Especially because every other method I tried worked perfectly, just not when I'm sending it from the NodeMcu. Any help is very much appreciated!
It turned out that there was an error in the server code.
Instead of
client.send(message, 0, message.length, rinfo.port, rinfo.ip);
it should have been
client.send(message, 0, message.length, rinfo.port, rinfo.address);
The server didn't know rinfo.ip, so it spammed itself with the same message over and over again.
I am trying to setup a communication between a python script (that will do a lot of computation on data that cannot be done in javascript and send send that data as a json) and a javascript client.
I have the following code for my python server:
import socket
import sys
from thread import *
HOST = '' # Symbolic name meaning all available interfaces
PORT = 9888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket created'
#Bind socket to local host and port
try:
s.bind((HOST, PORT))
except socket.error as msg:
print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
sys.exit()
print 'Socket bind complete'
#Start listening on socket
s.listen(10)
print 'Socket now listening'
#Function for handling connections. This will be used to create threads
def clientthread(conn):
#Sending message to connected client
conn.send('Welcome to the server. Type something and hit enter\n') #send only takes string
#infinite loop so that function do not terminate and thread do not end.
while True:
#Receiving from client
data = conn.recv(1024)
reply = 'OK...' + data
if not data:
break
conn.sendall(reply)
#came out of loop
conn.close()
#now keep talking with the client
while 1:
#wait to accept a connection - blocking call
conn, addr = s.accept()
print 'Connected with ' + addr[0] + ':' + str(addr[1])
#start new thread takes 1st argument as a function name to be run, second is the tuple of arguments to the function.
start_new_thread(clientthread ,(conn,))
s.close()
And the following code for my javascript client:
var connection = new WebSocket('ws://127.0.0.1:8999');
connection.onopen = function () {
connection.send('Hello'); // Send the message to the server
};
I get the following error from my javascript client:
Error during WebSocket handshake: net::ERR_INVALID_HTTP_RESPONSE
And the following output from my python server
Socket created
Socket bind complete
Socket now listening
Connected with 127.0.0.1:53956
Unhandled exception in thread started by <function clientthread at 0x10abac578>
Traceback (most recent call last):
File "server.py", line 71, in clientthread
data = conn.recv(1024)
socket.error: [Errno 54] Connection reset by peer
Would anyone know what is wrong?
Edit: forgot to mention that I have seen this SO Post before, but my problem is not the same, or rather should I say that the error encountered by OP is not the same as mine.
A WebSocket is a not the same as a plain TCP socket you create. WebSocket is a protocol on top of TCP instead which starts with a HTTP handshake and then continues with a framing based protocol. If you want to implement a WebSocket server in Python you need to implement this protocol as specified in RFC 6455 or use existing WebSocket libraries.
An example server-side python code using WebSocket is:
import asyncio
import websockets
async def handle_message(message):
print(message)
async def consumer_handler(websocket, path):
while True:
message = await websocket.recv()
await handle_message(message)
start_server = websockets.serve(consumer_handler, 'localhost', 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
I have implemented web socket using below code,
try {
console.log('wss://' + hostname + ':' + port + endpoint);
webSocket = new WebSocket(webSocketURL);
webSocket.onmessage = function (event) {
//console.log('send message successfully.');
var obj = JSON.parse(event.data);
append_data(obj, market_pair);
};
} catch (exception) {
console.log('Exception handle!');
console.error(exception);
}
My page is https supported so I am using wss protocol. but issue is it gives me error that "Firefox can’t establish a connection to the server at wss://domainname.com:4008/order.php"
if I will load the page using simple http and use ws in websocket then it is working fine but with wss it shows above error. same way in google chrome it will also not able to connect.
My ssl certificate is installed correctly in server and my hosting and domain provided in godaddy and they told me that certificate is installed correctly.
I am running server side socket in php and it is working fine just now able to connect to that socket port using wss://
I have found that I am not able to do handshak using my php code because my php file is running in background and the content I am getting by socket read is encrypted.
function doHandshake($received_header, $client_socket_resource, $host_name, $port) {
echo $received_header;
$headers = array();
$lines = preg_split("/\r\n/", $received_header);
foreach ($lines as $line) {
$line = chop($line);
if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
$headers[$matches[1]] = $matches[2];
}
}
$secKey = $headers['Sec-WebSocket-Key'];
echo $headers['Sec-WebSocket-Key'];
$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
//$secAccept = base64_encode(sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'));
$buffer = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
"Upgrade: websocket\r\n" .
"Connection: Upgrade\r\n" .
"WebSocket-Origin: $host_name\r\n" .
"WebSocket-Location: wss://$host_name:$port/websocket/btc_boon_pair.php\r\n" .
"Sec-WebSocket-Accept:$secAccept\r\n\r\n";
socket_write($client_socket_resource, $buffer, strlen($buffer));
}
this is my handshak function $headers['Sec-WebSocket-Key'] is undefined because $received_header is encrypted.
can anyone suggest the solution of this issue? I have to run php file as daemon and want to connect using wss protocol.
Ok finally I solved the issue.
due to the SSL encryption it is not able to read socket in my php daemon file. So I get solution from below answer
https://serverfault.com/questions/804862/apache-mod-proxy-forward-secure-websocket-to-non-secure
we can use proxy pass in apache so it will send all request with /websocket to the other ws://host:port/
after this it is working perfectly.
Also make sure that domain name is not using proxy because due to that my above solution is not worked but After removing proxy from DNS settings it started working.
In my case, I was using a VPS on Dreamhost.com and thus couldn't change Apache configuration files. However, I used this library to start secure socket server via following code:
use WSSC\Components\ServerConfig;
use WSSC\WebSocketServer;
$config = new ServerConfig();
$config->setIsSsl(true)->setAllowSelfSigned(true)
->setCryptoType(STREAM_CRYPTO_METHOD_SSLv23_SERVER)
->setLocalCert("./tests/certs/cert.pem")->setLocalPk("./tests/certs/key.pem")
->setPort(8888);
$websocketServer = new WebSocketServer(new ServerHandler(), $config);
$websocketServer->run();
I filled cert.pem and key.pem files with proper values from Dreamhost dashboard -> Websites -> Secure Certificates section.
i have python server(with the help of asyncio and websockets module) and js client(with the help of websockets library) that are connected. The problem is that i need this connection to be secured (i'm working on passwords), but i had no success on establishing a connection with wss(web socket secure) - the code runs only with ws.
I even tried to establish my own encryption with RSA and AES but that also didn't work.
i'm really hopeles about it so if anyone ever did it or know a little about it, pls help me figure out what's wrong with it, or a direction to a rigth solution for secured connection that will work.
here's my server:
async def app(websocket, path):
while True :
data = await websocket.recv()
if (data== "close"):
print("connection with client closed.")
break
data = data.encode()
arr = data.split("~".encode())
for i in range(0,4):
arr[i]=arr[i].decode()
resualt=algo(arr)
await websocket.send(resualt)
start_server = websockets.serve(app, '0.0.0.0', 6169)
and my client:
var socket = new WebSocket("ws://127.0.0.1:6169/");
socket.onopen = function (evt) {
socket.send(st);
};
socket.onmessage = function (evt) {
alert("scrool extension page down to see the password");
$('#res').val(evt.data);
socket.send("close");
};
socket.onerror = function (evt) {
alert("the error is: "+evt.data);
};
in the python script we tried to use ssl:
c = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
c.load_default_certs(purpose=ssl.Purpose.CLIENT_AUTH)
start_server = websockets.serve(app, '0.0.0.0', 6169, ssl=c)
and in the js sciprt we wrote instead of the ws, wss:
"ws://127.0.0.1:6169/")
and the error we get:
WebSocket connection to 'wss://127.0.0.1:6169/' failed: Error in connection
establishment: net::ERR_CONNECTION_CLOSED