Python | WebSocketServer

2018-12-22  本文已影响0人  T_K_233
'''
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()

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="author" content="-T.K.-">
  <meta name="copyright" content="-T.K.-">
  <title>Untitled Page</title>
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<canvas id="myCanvas" width="800" height="600" style="border:1px solid #000000;"></canvas>
<script>
  var Vec3 = function(x, y, z) {
    this.x = x;
    this.y = y;
    this.z = z;
  };

  var Player = function(id) {
    this.id = id;
    this.pos = new Vec3(0, 0, 0);
    
    this.get_data = function() {
      return JSON.stringify({id: this.id, pos: this.pos});
    }
  };
</script>
<script>
  var ws;
  $(document).ready(function() {
    if('WebSocket' in window) console.log('broswer support');
    
    ws = new WebSocket('ws://192.168.1.8:10002');
    ws.onopen = function() {
      console.log('socket connection established');
      ws.send(JSON.stringify({init: 1}));
      setInterval(function(){
        ws.send(player.get_data());
      }, 50);
    };
    ws.onmessage = function(res) {
      data = JSON.parse(res.data);
      console.log(data);
      if(data.init) {
        player = new Player(data.id);
      } else {
        ctx.clearRect(0, 0, c.width, c.height);
        for(p in data) {
          ctx.fillRect(data[p].pos.x, data[p].pos.y, 64, 64);
        }
      }
    };
    ws.onclose = function() {
      console.log('sock closed');
    };
  });
</script>
<script>
  var player;
  var status = 4;

  var c = document.getElementById('myCanvas');
  var ctx = c.getContext('2d');
  ctx.fillStyle = '#FF0000';

  $(document).keydown(function(e) {
    switch(e.keyCode) {
      case 37:
        player.pos.x -= 10;
        break;
      case 39:
        player.pos.x += 10;
        break;
      case 38:
        player.pos.y -= 10;
        break;
      case 40:
        player.pos.y += 10;
        break;
    }
  });
</script>
上一篇下一篇

猜你喜欢

热点阅读