1

I am trying to connect a html5 websocket to python. I have two files:

1. index.html

<script>
      window.onload = function() {
        var connection = new WebSocket("ws://localhost:9876/");
        connection.onopen = function () { 
            connection.send('Ping');
        };

        connection.onerror = function (error) {
            console.log('WebSocket Error ' + error);
        };

        connection.onmessage = function (e) {
            console.log('Server: ' + e.data);
        };

      };
    </script>

2. server.py

import socket

HOST = ''
PORT = 9876
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
conn, addr = s.accept()
print 'Connected by', addr
conn.send("Hello")
while 1:
    data = conn.recv(4096)
    print data
    if not data: break
    conn.sendall(data)
conn.close()

After starting server.py, it keeps running until I open index.html in my browser. When I do so, I get on the console the following:

WebSocket connection to 'ws://localhost:9876/' failed:
WebSocket Error [object Event]

However, on the other side(server.py) I get:

Connected by('127.0.0.1', 55460)GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: localhost:9876
Origin: null
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: WP0GtT0hrdX1bKa0DepOHQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36

Q What's wrong with my codes? How should I modify it so as to have biorder communication between them? I am looking for something like this:

if data == 'kill': conn.close()

important edit

If I change in the index.html WebSocket("ws://localhost:9876/") to WebSocket("ws://html5rocks.websocket.org/echo") than it works. I guess that means that to problem is on the server.py side

clue #2

If I write WebSocket("ws://localhost/:9876") in the html, the python code cannot proceed the line conn, addr = s.accept()

5
  • While server.py is running, what happens when you telnet to the socket? telnet localhost 9876 Commented Aug 14, 2013 at 20:16
  • @Mike I am sorry but don't know where to enter the code above? Commented Aug 14, 2013 at 20:20
  • @gen tellnet is a shell (command prompt) command, not code. It just tries to make a connection to whatever address, give it a try and upload the results. Commented Aug 14, 2013 at 20:24
  • @enginefree I meant command not code, however my cmd doesn't recognise command 'telnet' Commented Aug 14, 2013 at 20:43
  • 1
    Your probably on windows. In which case you can try this, fettesps.com/windows-7-enable-telnet Commented Aug 14, 2013 at 20:44

1 Answer 1

6

You haven't response headers. try ,

server.py

import socket, hashlib, base64, threading

class PyWSock:
    MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
    HSHAKE_RESP = "HTTP/1.1 101 Switching Protocols\r\n" + \
                "Upgrade: websocket\r\n" + \
                "Connection: Upgrade\r\n" + \
                "Sec-WebSocket-Accept: %s\r\n" + \
                "\r\n"
    LOCK = threading.Lock()

    clients = []

    def recv_data (self, client):
        # as a simple server, we expect to receive:
        #    - all data at one go and one frame
        #    - one frame at a time
        #    - text protocol
        #    - no ping pong messages
        data = bytearray(client.recv(512))
        if(len(data) < 6):
            raise Exception("Error reading data")
        # FIN bit must be set to indicate end of frame
        assert(0x1 == (0xFF & data[0]) >> 7)
        # data must be a text frame
        # 0x8 (close connection) is handled with assertion failure
        assert(0x1 == (0xF & data[0]))

        # assert that data is masked
        assert(0x1 == (0xFF & data[1]) >> 7)
        datalen = (0x7F & data[1])

        #print("received data len %d" %(datalen,))

        str_data = ''
        if(datalen > 0):
            mask_key = data[2:6]
            masked_data = data[6:(6+datalen)]
            unmasked_data = [masked_data[i] ^ mask_key[i%4] for i in range(len(masked_data))]
            str_data = str(bytearray(unmasked_data))
        return str_data

    def broadcast_resp(self, data):
        # 1st byte: fin bit set. text frame bits set.
        # 2nd byte: no mask. length set in 1 byte. 
        resp = bytearray([0b10000001, len(data)])
        # append the data bytes
        for d in bytearray(data):
            resp.append(d)

        self.LOCK.acquire()
        for client in self.clients:
            try:
                client.send(resp)
            except:
                print("error sending to a client")
        self.LOCK.release()

    def parse_headers (self, data):
        headers = {}
        lines = data.splitlines()
        for l in lines:
            parts = l.split(": ", 1)
            if len(parts) == 2:
                headers[parts[0]] = parts[1]
        headers['code'] = lines[len(lines) - 1]
        return headers

    def handshake (self, client):
        print('Handshaking...')
        data = client.recv(2048)
        headers = self.parse_headers(data)
        print('Got headers:')
        for k, v in headers.iteritems():
            print k, ':', v

        key = headers['Sec-WebSocket-Key']
        resp_data = self.HSHAKE_RESP % ((base64.b64encode(hashlib.sha1(key+self.MAGIC).digest()),))
        print('Response: [%s]' % (resp_data,))
        return client.send(resp_data)

    def handle_client (self, client, addr):
        self.handshake(client)
        try:
            while 1:            
                data = self.recv_data(client)
                print("received [%s]" % (data,))
                self.broadcast_resp(data)
        except Exception as e:
            print("Exception %s" % (str(e)))
        print('Client closed: ' + str(addr))
        self.LOCK.acquire()
        self.clients.remove(client)
        self.LOCK.release()
        client.close()

    def start_server (self, port):
        s = socket.socket()
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        s.bind(('', port))
        s.listen(5)
        while(1):
            print ('Waiting for connection...')
            conn, addr = s.accept()
            print ('Connection from: ' + str(addr))
            threading.Thread(target = self.handle_client, args = (conn, addr)).start()
            self.LOCK.acquire()
            self.clients.append(conn)
            self.LOCK.release()

ws = PyWSock()
ws.start_server(9876)

link

I think minimal server.py

import socket,hashlib,base64

MAGIC = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HSHAKE_RESP = "HTTP/1.1 101 Switching Protocols\r\n" + \
            "Upgrade: websocket\r\n" + \
            "Connection: Upgrade\r\n" + \
            "Sec-WebSocket-Accept: %s\r\n" + \
            "\r\n"

HOST = ''
PORT = 9876
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)
conn, addr = s.accept()
print 'Connected by', addr
data = conn.recv(4096)
headers = {}
lines = data.splitlines()
for l in lines:
    parts = l.split(": ", 1)
    if len(parts) == 2:
        headers[parts[0]] = parts[1]
headers['code'] = lines[len(lines) - 1]
key = headers['Sec-WebSocket-Key']
resp_data = HSHAKE_RESP % ((base64.b64encode(hashlib.sha1(key+MAGIC).digest()),))
conn.send(resp_data)

while 1:
    data = conn.recv(4096)
    if not data: 
        break
    databyte = bytearray(data)
    datalen = (0x7F & databyte[1])
    str_data = ''
    if(datalen > 0):
        mask_key = databyte[2:6]
        masked_data = databyte[6:(6+datalen)]
        unmasked_data = [masked_data[i] ^ mask_key[i%4] for i in range(len(masked_data))]
        str_data = str(bytearray(unmasked_data))
    print str_data
    resp = bytearray([0b10000001, len(str_data)])
    for d in bytearray(str_data):
        resp.append(d)
    conn.sendall(resp)
conn.close()

One time running. Because not threading.

Sign up to request clarification or add additional context in comments.

6 Comments

Could you please tell me what makes it working, which i didn't have in my original code?
websocket is running request headers and response headers. Yours server code haven't response headers.
I am sorry, could you please explain me what response headers are?
you can see "print('Response: [%s]' % (resp_data,))", "HTTP/1.1 101 Switching Protocols\r\n" + \ "Upgrade: websocket\r\n" + \ "Connection: Upgrade\r\n" + \ "Sec-WebSocket-Accept: %s\r\n" + \ "\r\n"
Actually, as I cannot understand your code, I would like to reach the most simple code that still works. Could you please tell me which parts are the most important? I don't need encryption for instance.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.