Python | WebSocketServer

'''
Host a multiplayer sever via WebSocket protocol
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
'''

import base64
import hashlib
import socket
from struct import pack, unpack
import threading
import json


class WebSocketConn:
    def __init__(self, conn):
        self.conn = conn
        request = self.conn.recv(1024).strip().decode('utf-8', 'ignore').split('\r\n')

        # parse headers into dict
        self.headers = dict([line.split(': ', 1) for line in request[1:]])

        # perform WebSocket handshake
        self._handshake()

    def _handshake(self):
        key = self.headers.get('Sec-WebSocket-Key') + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
        resp_key = base64.standard_b64encode(hashlib.sha1(key.encode()).digest()).decode()
        res_header = {
            'Upgrade': 'websocket',
            'Connection': 'Upgrade',
            'Sec-WebSocket-Accept': resp_key,
            }
        response = 'HTTP/1.1 101 Switching Protocols\r\n'
        for i in res_header:
            response += '%s: %s\r\n' % (i, res_header[i]) 
        response += '\r\n'
        self.conn.send(response.encode())

    def recv(self):
        '''
        retrieve data from the client.
        '''
        buffer = self.conn.recv(2)
        if buffer:
            # read the three possible content-length number
            length = buffer[1] - 2**7
            if length == 126:
                length, = unpack('>H', self.conn.recv(2))
            elif length == 127:
                length, = unpack('>Q', self.conn.recv(8))

            # get the masking key for the content
            mask = self.conn.recv(4)

            # encoded content
            buffer = self.conn.recv(length)

            decoded = ''
            for i in range(length):
                # decode the content
                decoded += chr(buffer[i] ^ mask[i % 4])
            return decoded

    def send(self, data):
        '''
        send content in form of WebSocket data frame.
        '''
        buffer = b''
        # initial 4 bits
        buffer += pack('>B', 129)

        # length of the content
        if len(data) > 126:
            if len(data) < 2 ** 10:
                buffer += pack('>BH', 126, len(data))
            else:
                buffer += pack('>BQ', 127, len(data))
        else:
            buffer += pack('>B', len(data))

        # append content
        buffer += data.encode()
        
        self.conn.send(buffer)

    def close(self):
        '''
        close the connection.
        '''
        self.conn.close()

class WebSocket:
    def __init__(self, addr):
        '''
        a WebSocket socket.
        @param addr: the address to bind with, i.e. (host, port)
        '''
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.sock.bind(addr)
        
    def listen(self, num):
        '''
        maximum clients to listen to.
        '''
        self.sock.listen(num)
        
    def accept(self):
        '''
        accept the connection from a client.
        '''
        conn, self.addr = self.sock.accept()
        self.conn = WebSocketConn(conn)
        return self.conn, self.addr


class WebSocketServer:
    def __init__(self, sock):
        '''
        a WebSocket server class with multithreading.
        '''
        self.sock = sock
        self.player_pool = []

    def run(self):
        '''
        run server.
        '''
        # lock for controlling the player pool
        lock = threading.Lock()
        while True:
            conn, addr = self.sock.accept()
            threading.Thread(target=self.handle, args=(conn, addr, lock)).start()

    def handle(self, conn, addr, lock):
        while True:
            try:
                buffer = conn.recv()
                if buffer:
                    try:
                        req = json.loads(buffer.strip())
                    except Exception as e:
                        print(e)
                    if req.get('init'):
                        conn.send(json.dumps({'init': 1, 'id': len(self.player_pool)}))
                    else:
                        if req.get('id') >= len(self.player_pool):

                            lock.acquire()
                            self.player_pool.append(req)
                            lock.release()
                        else:
                            self.player_pool[req.get('id')] = req
                        conn.send(json.dumps(self.player_pool))
                    
            except Exception as e:
                conn.close()
                return 0


if name == '__main__':
    sock = WebSocket(('0.0.0.0', 10002))
    sock.listen(10)
    server = WebSocketServer(sock)
    server.run()




  
  
  
  Untitled Page
  






你可能感兴趣的:(Python | WebSocketServer)