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()

<!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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容