一、什么是 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\r\n\
Upgrade: WebSocket\r\n\
Connection: Upgrade\r\n\
Sec-WebSocket-Accept: %s\r\n\r\n' % 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('\r\n\r\n', 1)
      for line in header.split('\r\n')[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 += "\r\n" +new Date().Format("yyyy/MM/dd hh:mm:ss")+ ": " + msg;
      }
       function onkey(event) { if (event.keyCode == 13) { send(); } }
   </script>
</html>

运行效果:

Python中Websocket的实现及基本原理的更多相关文章

  1. python 实现websocket

    python中websocket需要我们自己实现握手代码,流程是这样:服务端启动websocket服务,并监听.当客户端连接过来时,(需要我们自己实现)服务端就接收客户端的请求数据,拿到请求头,根据请 ...

  2. Python核心技术与实战——十三|Python中参数传递机制

    我们在前面的章节里学习了Python的函数基础以及应用,那么现在想一想:传参,也就是把一些参数从一个函数传递到另一个函数,从而使其执行相应的任务,这个过程的底层是如何工作的,原理又是怎样的呢? 在实际 ...

  3. Python中的WebSocket

    一.Websockets介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...

  4. 量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python)(转)

    量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python) 原文地址:http://blog.csdn.net/u012234115/article/details/728300 ...

  5. python实现websocket服务器,可以在web实时显示远程服务器日志

    一.开始的话 使用python简单的实现websocket服务器,可以在浏览器上实时显示远程服务器的日志信息. 之前做了一个web版的发布系统,但没实现在线看日志,每次发布版本后,都需要登录到服务器上 ...

  6. Python中的默认参数(转)

    add by zhj: Python设计者为何将默认参数设计成这样呢?参见Python函数参数默认值的陷阱和原理深究 原文:https://github.com/acmerfight/insight_ ...

  7. [Python]通过websocket与jsclient通信

    站点大多使用HTTP协议通信.而HTTP是无连接的协议.仅仅有client请求时,server端才干发出对应的应答.HTTP请求的包也比較大,假设仅仅是非常小的数据通信.开销过大.于是,我们能够使用w ...

  8. python中简化的验证码功能

    验证码一般用来验证登陆.交易等行为,减少对端为机器操作的概率,python中可以使用random模块,char()内置函数来实现一个简单的验证码功能. import random def veri_c ...

  9. Python面试-websocket及web框架

    一.Websocket 1. websocket概念 在讲websocket之前,我们先来看看ajax轮询和long poll的实现机制. A.  ajax轮询 ajax轮询的原理非常简单,让浏览器隔 ...

  10. 深入理解python(一)python语法总结:基础知识和对python中对象的理解

    用python也用了两年了,趁这次疫情想好好整理下. 大概想法是先对python一些知识点进行总结,之后就是根据python内核源码来对python的实现方式进行学习,不会阅读整个源码,,,但是应该会 ...

随机推荐

  1. absolute定位后居中的方法

    要求 : 子级需要加动画上下动起来,并且需要在全屏居中 html代码如下: <div class="row m-0 w-100 mybanner"> <!-- 左 ...

  2. BC7-牛牛的字符矩形

    题目描述 牛牛尝试用键盘读入一个字符,然后在屏幕上显示用这个字符组成的 3*3 的矩形. 输入描述 一行读入一个 char 类型的字符. 输出描述 输出这个字符组成的 3*3 矩形. 示例 1 输入: ...

  3. vue3和百度地图关键字检索 定位 点击定位

    效果图 在index.html中引入 百度地图开放平台 去申请你的ak 非常的简单可以自己百度 一下 <!-- 这个用官网给的有好多警告 更具百度的把 https://api.map.baidu ...

  4. continue语句-死循环

    continue语句 continue 使用场景:结束本次循环,继续下一次的循环 public static void main(String[] args) { for (int i = 1; i ...

  5. Python调用Openstack API

    本文将介绍如何使用 python 调用 OpenStack API. 什么是RESTful API RESTful API 就是 RESTful 风格的 API.遵循 RESTful 风格开发的API ...

  6. Selenium CHANGELOG[最新版本4.8.0 计划中]

    Selenium CHANGELOG[持续更新] 源文件 https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES 搬运工 对重点版本做 ...

  7. 高精度计算模板 -感谢acwing

    高精度加 1 // C = A + B, A >= 0, B >= 0 2 vector<int> add(vector<int> &A, vector&l ...

  8. P21_事件传参与数据同步

    事件绑定 在事件处理函数中为 data 中的数据赋值 通过调用 this.setData(dataObject) 方法,可以给页面 data 中的数据重新赋值,示例如下: 事件传参 小程序中的事件传参 ...

  9. Java期末测试

    会议预约管理信息系统(50分)     1.项目背景: 会议是企业进行决策.协商的重要组织形式,是企业日常办公处理事务的重要手段,是办公流程中不可缺少的重要环节,作为企业,如何有效的进行会议组织,管理 ...

  10. 【python自动化测试】1- python基础和pycharm使用

    python基础和pycharm使用 1.pycharm 1.1 为特定格式文件添加头信息 File--Settings--Editor--File and Code Templates--Pytho ...