• Python中Websocket的实现及基本原理


    一、什么是 WebSocket ?

     

    WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。

    以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较大。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。

    而 WebSocket 解决了 HTTP 的这几个难题。当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通信,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。

     

     

     

     

    主要使用场景:

     

    没有其他能像 WebSocket 一样实现全双工传输的技术了,迄今为止,大部分开发者还是使用 Ajax 轮询来实现,但这是个不太优雅的解决办法,WebSocket 虽然用的人不多,可能是因为协议刚出来的时候有安全性的问题以及兼容的浏览器比较少,但现在都有解决。如果你有这些需求可以考虑使用 WebSocket:

     

    1. 多个用户之间进行交互;

    2. 需要频繁地向服务端请求更新数据。

     

    比如弹幕、消息订阅、多玩家游戏、协同编辑、股票基金实时报价、视频会议、在线教育等需要高实时的场景。

     

    **主要还是:消息推送

     

     

     

    实现一个简单的聊天室程序,代码如下:

    #-*- coding:utf8 -*-

    import threading
    import hashlib
    import socket
    import base64

    global clients
    clients = {}

    #通知客户端
    def notify(message):
      for connection in clients.values():
          connection.send('%c%c%s' % (0x81, len(message), message))

    #客户端处理线程
    class websocket_thread(threading.Thread):
      def __init__(self, connection, username):
          super(websocket_thread, self).__init__()
          self.connection = connection
          self.username = username
       
      def run(self):
          print 'new websocket client joined!'
          data = self.connection.recv(1024)
          headers = self.parse_headers(data)
          token = self.generate_token(headers['Sec-WebSocket-Key'])
          self.connection.send('
    HTTP/1.1 101 WebSocket Protocol Hybi-10
    Upgrade: WebSocket
    Connection: Upgrade
    Sec-WebSocket-Accept: %s ' % token)
          while True:
              try:
                  data = self.connection.recv(1024)
              except socket.error, e:
                  print "unexpected error: ", e
                  clients.pop(self.username)
                  break
              data = self.parse_data(data)
              if len(data) == 0:
                  continue
              message = self.username + ": " + data
              notify(message)
               
      def parse_data(self, msg):
          v = ord(msg[1]) & 0x7f
          if v == 0x7e:
              p = 4
          elif v == 0x7f:
              p = 10
          else:
              p = 2
          mask = msg[p:p+4]
          data = msg[p+4:]
          return ''.join([chr(ord(v) ^ ord(mask[k%4])) for k, v in enumerate(data)])
           
      def parse_headers(self, msg):
          headers = {}
          header, data = msg.split(' ', 1)
          for line in header.split(' ')[1:]:
              key, value = line.split(': ', 1)
              headers[key] = value
          headers['data'] = data
          return headers

      def generate_token(self, msg):
          key = msg + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
          ser_key = hashlib.sha1(key).digest()
          return base64.b64encode(ser_key)

    #服务端
    class websocket_server(threading.Thread):
      def __init__(self, port):
          super(websocket_server, self).__init__()
          self.port = port

      def run(self):
          sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
          sock.bind(('127.0.0.1', self.port))
          sock.listen(5)
          print 'websocket server started!'
          while True:
              connection, address = sock.accept()
              try:
                  username = "ID" + str(address[1])
                  thread = websocket_thread(connection, username)
                  thread.start()
                  clients[username] = connection
              except socket.timeout:
                  print 'websocket connection timeout!'

    if __name__ == '__main__':
      server = websocket_server(9000)
      server.start()

     

    测试页面:

    <!--
    @http://www.cnblogs.com/zhuweisky/p/3930780.html
    -->
    <!DOCTYPE html>
    </html>
       <head>
           <meta charset="utf-8">
       </head>
       <body>
           <h3>WebSocketTest</h3>
           <div id="login">
               <div>
                   <input id="serverIP" type="text" placeholder="服务器IP" value="127.0.0.1" autofocus="autofocus" />
                   <input id="serverPort" type="text" placeholder="服务器端口" value="9000" />
                   <input id="btnConnect" type="button" value="连接" onclick="connect()" />
               </div>
               <div>
                   <input id="sendText" type="text" placeholder="发送文本" value="I'm WebSocket Client!" />
                   <input id="btnSend" type="button" value="发送" onclick="send()" />
               </div>
               <div>
                   <div>
                      来自服务端的消息
                   </div>
                   <textarea id="txtContent" cols="50" rows="10" readonly="readonly"></textarea>
               </div>
           </div>
       </body>
       <script>
           var socket;

           function connect() {
               var host = "ws://" + $("serverIP").value + ":" + $("serverPort").value + "/"
               socket = new WebSocket(host);
               try {

                   socket.onopen = function (msg) {
                       $("btnConnect").disabled = true;
                       alert("连接成功!");
                  };

                   socket.onmessage = function (msg) {
                       if (typeof msg.data == "string") {
                           displayContent(msg.data);
                      }
                       else {
                           alert("非文本消息");
                      }
                  };

                   socket.onclose = function (msg) { alert("socket closed!") };
              }
               catch (ex) {
                   log(ex);
              }
          }

           function send() {
               var msg = $("sendText").value
               socket.send(msg);
          }

           window.onbeforeunload = function () {
               try {
                   socket.close();
                   socket = null;
              }
               catch (ex) {
              }
          };

           function $(id) { return document.getElementById(id); }

           Date.prototype.Format = function (fmt) { //author: meizz
               var o = {
                   "M+": this.getMonth() + 1, //月份
                   "d+": this.getDate(), //日
                   "h+": this.getHours(), //小时
                   "m+": this.getMinutes(), //分
                   "s+": this.getSeconds(), //秒
                   "q+": Math.floor((this.getMonth() + 3) / 3), //季度
                   "S": this.getMilliseconds() //毫秒
              };
               if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
               for (var k in o)
                   if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
               return fmt;
          }

           function displayContent(msg) {
               $("txtContent").value += " " +new Date().Format("yyyy/MM/dd hh:mm:ss")+ ": " + msg;
          }
           function onkey(event) { if (event.keyCode == 13) { send(); } }
       </script>
    </html>

     

     

    运行效果:

     

  • 相关阅读:
    python使用数据库的一些操作
    正则表达式整理
    tomcat启动成功但是访问不到官网
    控制反转 依赖注入 AOP 和 IOC
    abstract 和 interface区别
    input文本框 鼠标点击默认消失,不输入离开鼠标恢复默认值
    声明式管理事务:基于AspectJ的xml方式
    使用注解配置声明式事务
    spring事物 (mu课)
    oracle表之数据类型
  • 原文地址:https://www.cnblogs.com/cs2612109/p/10815610.html
Copyright © 2020-2023  润新知