WebSocket双向通信——构建HTML5和Python的WebSocket服务器

58 阅读2分钟

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许客户端和服务器在没有HTTP请求的情况下相互传递数据,从而实现更实时的数据传输。本文将重点关注如何构建一个HTML5和Python的WebSocket服务器,并实现双向通信。

2、解决方案

实现HTML5和Python的WebSocket服务器双向通信的步骤如下:

  • 编写HTML5客户端代码:使用JavaScript编写HTML5客户端代码,用于在浏览器中建立WebSocket连接并向服务器发送和接收数据。
  • 编写Python服务器代码:使用Python编写服务器端代码,用于接收和处理来自客户端的WebSocket连接,并向客户端发送数据。
  • 建立WebSocket连接:客户端代码和服务器代码都已编写后,就可以建立WebSocket连接了。客户端代码可以通过调用WebSocket()方法来建立连接,而服务器代码可以通过调用accept()方法来接受连接。
  • 发送和接收数据:连接建立后,客户端和服务器就可以使用send()方法和recv()方法来发送和接收数据。

下面是详细的代码示例:

  • 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>
  • server.py
import socket, 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):
        data = bytearray(client.recv(512))
        if(len(data) < 6):
            raise Exception("Error reading data")

        assert(0x1 == (0xFF & data[0]) >> 7)
        assert(0x1 == (0xF & data[0]))

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

        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):
        resp = bytearray([0b10000001, len(data)])

        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):
        data = client.recv(2048)
        headers = self.parse_headers(data)

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

    def handle_client (self, client, addr):
        self.handshake(client)
        try:
            while 1:            
                data = self.recv_data(client)
                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):
            conn, addr = s.accept()
            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)

上述代码示例中,HTML5客户端代码用于建立WebSocket连接并向服务器发送和接收数据,而Python服务器代码用于接收和处理来自客户端的WebSocket连接,并向客户端发送数据。通过这种方式,实现了HTML5和Python的WebSocket服务器双向通信。