WebSocket javascript client and python server. Retrieving garbage in output - javascript

I am trying to send string/text data from browser client to python server and simply print it out. I have followed several examples on the internet, and all are the same: by using javascript
web_socket.send("text to be sent")
and (python)
data = web_socket.recv(1024)
print data
they receive what they want, what is clear and nice printout "text to be sent" on server site.
You can find my .html and .py below:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script src="jquery.js"></script>
<script type="application/javascript">
var ws;
function init() {
var servermsg = document.getElementById("servermsg");
ws = new WebSocket("ws://127.0.0.1:9877/");
ws.onopen = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server connected";
};
ws.onmessage = function(e){
servermsg.innerHTML = servermsg.innerHTML + "<br><< Recieved data: " + e.data;
};
ws.onclose = function(){
servermsg.innerHTML = servermsg.innerHTML + "<br>Server disconnected";
};
}
function postmsg(){
var text = document.getElementById("message").value;
ws.send(text);
servermsg.innerHTML = servermsg.innerHTML + "<br>>> Data sent: " + text;
}
//$(function(){
// var text = document.getElementById("message").value;
// ws.send(text);
// servermsg.innerHTML = servermsg.innerHTML + "<br>Sent: " + text;
//});
</script>
</head>
<body onload="init();">
<form action="" onSubmit="postmsg();return false;">
<input type="text" name="message" value="" id="message">
<input type="submit" name="submit" value="" id="submit">
</form>
<div id="servermsg"><h1>Message log:</h1></div>
</body>
</html>
Server:
#!/usr/bin/env python
import socket
import threading
import struct
import hashlib
import base64
PORT = 9877
_address = ""
def create_handshake_resp(handshake):
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(": ")
if parts[0] == "Sec-WebSocket-Key":
key = parts[2]
magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
accept_key = base64.b64encode(hashlib.sha1(key+magic).digest())
return (
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: " + accept_key + "\r\n\r\n")
def handle(s, addr):
data = s.recv(1024)
response = create_handshake_resp(data)
s.sendto(response, addr)
lock = threading.Lock()
while 1:
print "Waiting for data from", addr
data = s.recv(1024)
print "Done"
if not data:
print "No data"
break
print 'Data from', addr, ':', data
print 'Client closed:', addr
lock.acquire()
clients.remove(s)
lock.release()
s.close()
def start_server():
print 'STARTING SERVER...'
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
s.listen(1)
print 'SERVER STARTED'
while 1:
conn, addr = s.accept()
print 'NEW CONNECTION ['+str(len(clients))+'], connected by ', addr
clients.append(conn)
threading.Thread(target = handle, args = (conn, addr)).start()
clients = []
start_server()
And server printout (when input was like "AA", or "ABC"):
STARTING SERVER...
SERVER STARTED
NEW CONNECTION [0], connected by ('127.0.0.1', 43877)
Waiting for data from ('127.0.0.1', 43877)
Done
Data from ('127.0.0.1', 43877) : ����w�q
Waiting for data from ('127.0.0.1', 43877)
Done
Data from ('127.0.0.1', 43877) : ��)B�h
Waiting for data from ('127.0.0.1', 43877)

I'm working on something similar myself. The Websocket protocol mandates that the client sends all its data using a mask. This is why you see 'garbage' - it's the masked text.
https://www.rfc-editor.org/rfc/rfc6455#section-5
"a client MUST mask all frames that it
sends to the server"
Read section 5 of the protocol and all will become clear. The browser (ie the client) is just implementing the protocol as it should (when you call ws.send). You need to do your bit.
Note also that when the sever sends data to the client it must NOT mask. But it still has to supply other info before the actual data (type, length etc).

To send a message from server side to websocket client you need to do as follows:
message = bytearray([0b10000001, len(original_msg)])
for byte in bytearray(original_msg):
message.append(byte)
See a stable server to client unidirectional socket library at https://github.com/westial/SingleSocket

The problem with the junk data was the javascript code sends the masked data and you must unmask it on the server side and the server sendes the unmasked data to client side.To solve this problem see my git-hub page
[][1]https://github.com/mohanbe/web-chat

Related

Concise example of how to join and leave rooms using Flask and Socket.io?

I'm trying to use socket.io with a Flask server connecting to JS.. I'm struggling with basically everything, but my first step is to make it so that users can connect to different channels. My broadcast message function is working, but when I click on a different channel, the messages do not get sent to a different channel.. What am I doing wrong?
JS:
document.addEventListener('DOMContentLoaded', ()=>{
// Send user back to login page if they didn't sign in
const username = localStorage.getItem('username');
if (username == null){
window.location = "/";
}
// Switch button active class when clicked
$('.list-group .list-group-item.list-group-item-action').click(function(e) {
$('.list-group .list-group-item.list-group-item-action.active').removeClass('active');
var $this = $(this);
if (!$this.hasClass('active')) {
$this.addClass('active');
}
e.preventDefault();
});
// Connect to socket.io
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
// Automatically connect to general channel
socket.emit('join',{"channel": "general", "username":username});
// When a channel is clicked, connect to that channel
document.querySelectorAll('.list-group-item').forEach(function(channel){
channel.onclick = () =>{
socket.emit('join',{"channel":channel.innerHTML, "username":username});
return false;
}
});
// When a message is sent, call 'send message' function from server
document.querySelector('#send-message').onsubmit = () => {
const message = document.querySelector('#m').value
socket.emit('send message', {'message': message});
// Clear message form
document.querySelector('#m').value = "";
return false;
};
});
// Callback from server for sending messages
socket.on('broadcast message', data =>{
console.log(data);
// Append message to list of messages
const li = document.createElement('li');
li.innerHTML = `${data.message}`;
document.querySelector('#messages').append(li);
});
});
Python Flask:
import os
from flask import Flask, render_template, url_for
from flask_socketio import SocketIO, emit, join_room, leave_room
from collections import defaultdict
app = Flask(__name__)
app.config["SECRET_KEY"] = os.getenv("SECRET_KEY")
socketio = SocketIO(app)
messages = defaultdict(list)
channels = ["Programming"]
#app.route("/")
def index():
return render_template("login.html")
#app.route("/chatroom/")
def chatroom():
return render_template("chatroom.html", channels=channels, messages=messages)
#socketio.on("send message")
def message(data):
print(data)
emit("broadcast message", {"message": message}, broadcast=True)
#socketio.on('join')
def on_join(data):
username = data['username']
channel = data['channel']
join_room(channel)
#send(username + ' has entered the room.', channel=channel)
if __name__ == '__main__':
socketio.run(app)
Think of a room as an array of users that stays on the server. When you send your message in "send message", you set broadcast=True, so it sends it as a global message to all users, as long as they are connected. If you only want to send to users in specific rooms, you will need to specify which room you want to send the message to from the client, each time you send a message, like this:
// client.js
socket.emit('join', { 'channel': channel, ... });
socket.emit('send message', {'message': message, 'channel': channel});
// server.py
#socketio.on("send message")
def message(data):
room = data['channel']
emit('broadcast message', data['message'], room=room)
I believe the first answer is correct. Just as a note, avoid using 'innerhtml' where possible especially in this case. By setting the innerhtml, anything a user writes in a message will be treated as html at the other end. This includes script tags which means someone could remotely run javascript on someone else's machine by sending a malicious message.
I would suggest using innerText or textContent. This will treat the message as plain text not html. They are slightly different so it may be worth looking into which one you need.
I would have done this as a comment but my rep isn't high enough.
tl:dr Use textContent or innerText instead of innerhtml.

How to save incoming textfile from client from websocket connection

I'm trying to implement a websocket server in node.js without using any framework.
Sending messages from client to server is working fine. But now I tried to send a text file from client to server. I can see the content on the server side by using console.log in the terminal.
But:
how can i get the file information ? (name, created/edited date, etc. ?)
how can i save the file ?
Client code:
(function () {
'use strict';
var output, ws;
//Display logging information in the document
function log(s) {
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
//Also log information on the javascript console
window.console.log(s);
}
//Send a message on the Websocket
function sendMessage(msg) {
console.log(ws.binaryType);
ws.send(msg);
console.log("Message sent");
}
//Initialize WebSocket connection and event handlers
function setup() {
output = document.getElementById("output");
ws = new window.WebSocket("ws://localhost:9999/");
//Listen for the connection open event then call the sendMessage function
ws.onopen = function () {
console.log("Connected");
document.getElementById('fl').onchange = function() {
sendMessage(document.querySelector('input[type="file"]').files[0]);
};
sendMessage("Hello Galileo!");
}
//Listen for the close connection event
ws.onclose = function (e) {
if(this.readyState == 2){
console.log('Connection is closing (Closing Handshake).')
}
else if(this.readyState == 3){
console.log('Connection closed. Has been closed or could not be opened.')
}
else{
console.log('Unhandled ReadyState: ',this.readyState);
}
console.log("Disconnected: " +
' reason:' + e.reason +
' was clean:' + e.wasClean +
' code:' + e.code);
}
//Listen for connection errors
ws.onerror = function (e) {
console.log("Error: " + e);
}
//Listen for new messages arriving at the client
ws.onmessage = function (e) {
console.log("Message received: " + e.data);
//Close the socket once one message has arrived
ws.close();
}
}
//Start running the example
setup();
})();
HTML Code
<!DOCTYPE html>
<html>
<head>
<title>Websocket Echo Client</title>
<meta charset="utf-8"/>
</head>
<body>
<h2>Websocket Echo Client</h2>
<div id="output"></div>
<input type="file" id="fl"/>
<script src="websocket.js"></script>
</body>
</html>
Server code
switch (opcode) {
case opcodes.TEXT:
this.payload = payload.toString("utf8");
winston.log('info','Text:\r\n', this.payload);
break;
case opcodes.BINARY:
console.log('info','File:\r\n', payload.toString("utf8"));
As far as I know the payload you're receiving on the server side does not contain the meta data about the file. I believe the File object is treated as a normal Blob with some extra meta data and the ws.send is only handling it like a Blob (it has no special handling for File).
The meta data can be accessed using
document.querySelector('input[type="file"]').files[0].name
document.querySelector('input[type="file"]').files[0].size
document.querySelector('input[type="file"]').files[0].type
And then send separately.

Node.JS sending wrong data through Socket.IO

I'm using Node.JS and Socket.IO to trade data. I have the following "schema":
A "local" Socket.IO server, which have a setInterval of 10 seconds that broadcasts different data in each iteration.
A "global" Socket.IO server, which, on a request from HTML clients, receives data from the "local" server (ioLocal variable in the following code is a socket.io-client), modifies the data with the number passed through the HTML client request and sends to the client.
The problem is that the HTML clients are receiving wrong data. Imagine that for a timestamp, the local server broadcasts the message "2". Meanwhile, two different HTML clients ask for data, the first one with "2" and the second one with "3". The expected is that the first one receives "4" (2*2) and the second one receives "6" (2*3), but is not working.
Instead, the first one receives "4" and the second one receives "12" (4*3). If I had a third client which sends "2", it would receive "24" (12*2).
I tried to create a different var for each client, use socket.set and socket.get, an array of socket.id's but none of these solutions worked.
What's the problem with this? Is it a scope problem?
Thanks for your time.
"Global" Server
var ioGlobal = require('socket.io').listen(9090);
ioGlobal.set('log level', 1);
ioGlobal.enable('browser client minification'); // send minified client
ioGlobal.enable('browser client etag'); // apply etag caching logic based on version number
ioGlobal.enable('browser client gzip'); // gzip the file
ioGlobal.set('transports', [ 'websocket', 'xhr-polling' ]);
ioGlobal.set("polling duration", 10);
ioGlobal.set('sync disconnect on unload', true);
var ioLocal = require('socket.io-client').connect('http://10.0.0.219:9091', {
'connect timeout' : 1500,
'reconnect' : true,
'reconnection delay' : 500,
'max reconnection attempts' : 20
});
ioGlobal.sockets.on('connection', function(iSocket) {
debug && console.log('Client '+iSocket.id+' connected.');
iSocket.on('disconnect', function() {
debug && console.log("Client "+iSocket.id+" disconnected.");
});
iSocket.on('data_rt', function(num) {
ioLocal.on('data_broadcast', function(data) {
//do something to data with num
data = data*num;
iSocket.emit('rsp_data_rt', data);
});
});
});
"Local" Server
var ioLocal = require('socket.io').listen(9091);
ioLocal.sockets.emit('data_broadcast', 2);
HTML Client
<!DOCTYPE html5>
<html>
<head>
<title>Socket.IO MultiClient Test</title>
<script type="text/javascript" src="http://10.0.1.180:3000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://10.0.0.219:9090');
function go(){
var message = document.getElementById('message').value;
socket.emit('data_rt', message);
};
socket.on('rsp_data_rt', function(num){
document.getElementById('result').innerHTML = num;
})
</script>
</head>
<body>
<h1>Socket.IO Multiclient</h1>
Message: <input type="text" id="message"><br />
<button type="button" onclick='go()'>Send</button>
Result: <span id="result"></span>
</body>
</html>

Python WebSocket not working

I tried to implement my first websocket example but I cannot make it work.
I use a python webserver:
import threading
import socket
def start_server():
tick = 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 1234))
sock.listen(100)
while True:
print 'listening...'
csock, address = sock.accept()
tick+=1
print 'connection!'
handshake(csock, tick)
print 'handshaken'
while True:
interact(csock, tick)
tick+=1
def send_data(client, str):
#_write(request, '\x00' + message.encode('utf-8') + '\xff')
str = '\x00' + str.encode('utf-8') + '\xff'
return client.send(str)
def recv_data(client, count):
data = client.recv(count)
return data.decode('utf-8', 'ignore')
def handshake(client, tick):
our_handshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"+"Upgrade: WebSocket\r\n"+"Connection: Upgrade\r\n"+"WebSocket-Origin: http://localhost:8888\r\n"+"WebSocket-Location: "+" ws://localhost:1234/websession\r\n\r\n"
shake = recv_data(client, 255)
print shake
#We want to send this without any encoding
client.send(our_handshake)
def interact(client, tick):
data = recv_data(client, 255)
print 'got:%s' %(data)
send_data(client, "clock ! tick%d" % (tick))
send_data(client, "out ! %s" %(data))
if __name__ == '__main__':
start_server()
And HTML:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Web Socket Example</title>
<meta charset="UTF-8">
<script>
window.onload = function() {
var s = new WebSocket("ws://localhost:1234/");
s.onopen = function(e) { s.send('Ping'); }
s.onmessage = function(e) { alert("got: " + e.data); }
s.onclose = function(e) { alert("closed"); }
};
</script>
</head>
<body>
<div id="holder" style="width:600px; height:300px"></div>
</body>
</html>
When I point my browser to http://localhost/websocket.html over apache2
I got the following error:
python websocketserver.py
listening...
connection!
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:1234
Origin: http://localhost
Sec-WebSocket-Key: A4sVkUhjVlTZbJrp2NUrqg==
Sec-WebSocket-Version: 13
handshaken
got:
Traceback (most recent call last):
File "websocketserver.py", line 43, in <module>
start_server()
File "websocketserver.py", line 17, in start_server
interact(csock, tick)
File "websocketserver.py", line 40, in interact
send_data(client, "out ! %s" %(data))
File "websocketserver.py", line 24, in send_data
return client.send(str)
socket.error: [Errno 32] Broken pipe
Can someone help me to fix this?
Thanks
You're responding with the older Hixie 75 protocol but the client only speaks the newer HyBi/IETF RFC 6455 WebSocket protocol.
Your response to the handshake should look more like this (the accept value is calculated from the key value from the client):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
In HyBi/6455, the frames are no longer delimited with \x00 and \xff. Instead there is a header to every frame that contains several pieces of data including frame type and payload length.
See the spec for more information. Or better yet, you could refer and/or use an existing python WebSocket implementation such as pywebsocket, tornado, or my own project websockify which contains websocket.py which is a generic websocket server lib.

Keeping the WebSocket connection alive

I'm doing a study on WebSocket protocol and trying to implement a simple ECHO service for now with Python on the backend.
It seems to work fine but the connection drops right after being established.
Here is my client:
<!doctype html>
<head>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function Client()
{
//var ws = new WebSocket("ws://echo.websocket.org"); // this works fine
var ws = new WebSocket("ws://localhost:8000");
ws.onopen = function(e){ $("#response").append(">> Connected<br />"); }
ws.onclose = function(e){ $("#response").append(">> Disconnected<br />"); }
ws.onerror = function(e){ $("#response").append(">> ERROR: " + e.data + "<br />"); }
ws.onmessage = function(e){ $("#response").append("> " + e.data + "<br />"); }
this.sendCmd = function()
{
var message = $("#cmd").val();
$("#response").append(message + "<br />");
ws.send(message);
return false;
}
this.disconnect = function()
{
ws.close();
}
}
// onload
$(function() {
$("#response").append(">> Connecting<br />");
client = new Client();
$("#send").click(client.sendCmd);
$("#disconnect").click(client.disconnect);
});
</script>
</head>
<body>
<input type="text" name="cmd" id="cmd" /> | Send | Disconnect<br />
<hr />
<span id="response"></span>
</body>
</html>
Here is the server:
import SocketServer
import socket
from hashlib import sha1
from base64 import b64encode
PORT = 8000
MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
class Handler(SocketServer.BaseRequestHandler):
# incoming connection
def setup(self):
self.data = self.request.recv(1024).strip()
print "connection established", self.client_address
self.headers = self.headsToDict(self.data.split("\n"))
# incoming message
def handle(self):
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + MAGIC).hexdigest().decode('hex'))
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" # "HTTP/1.1 101 Switching Protocols\r\n"
print "< HTTP/1.1 101 Web Socket Protocol Handshake" # "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
print "< Upgrade: websocket"
response += "Connection: Upgrade\r\n"
print "< Connection: Upgrade"
response += "Sec-WebSocket-Accept: "+accept+"\r\n\r\n"
print "< Sec-WebSocket-Accept: "+accept
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
# connection dropped
def finish(self):
print "connection lost", self.client_address
# convert a list of headers to a dictionary for convenience
def headsToDict(self, hdata):
rzygi = {}
for item in hdata:
print '>', item
item = item.split(':')
if len(item) > 1:
rzygi[item[0].strip()] = item[1].strip()
return rzygi
server = SocketServer.TCPServer(("", PORT), Handler)
server.socket_type = socket.SOCK_STREAM # didnt help
print "serving at port", PORT
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()
As mentioned, the connection is established successfully but then drops straight away, which makes me think the code is correct but there is something missing to keep the socket open. Here is the server output:
serving at port 8000
connection established ('127.0.0.1', 52633)
> GET / HTTP/1.1
> Upgrade: websocket
> Connection: Upgrade
> Host: localhost:8000
> Sec-WebSocket-Origin: http://localhost
> Sec-WebSocket-Key: qWGnhdFQ6l8Xs9awgQURfA==
> Sec-WebSocket-Version: 8
< HTTP/1.1 101 Web Socket Protocol Handshake
< Upgrade: websocket
< Connection: Upgrade
< Sec-WebSocket-Accept: fei4E4LQvPnf4y2ilebVsxRofvc=
connection lost ('127.0.0.1', 52633)
How do I keep the socket open?
edit: server code comments
The connection is closed each time after handle. You should rather stay there reading incoming data:
# incoming connection
def setup(self):
print "connection established", self.client_address
def handle(self):
while 1:
try:
self.data = self.request.recv(1024).strip()
# incoming message
self.headers = self.headsToDict(self.data.split("\r\n"))
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + MAGIC).hexdigest().decode('hex'))
response = "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
response += "Connection: Upgrade\r\n"
response += "Sec-WebSocket-Accept: "+accept+"\r\n\r\n"
print response
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
except:
print "except"
break

Categories

Resources