一、什么是 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. Java基础学习笔记-数据类型、数制

    数据类型,跟JS感觉差异不是很大,但是有个String不是很一样的样子 数据类型分为 基本数据类型和复合数据类型 基本数据类型分为下面三种 数值类型 1.整数类型:byte,short,int,lon ...

  2. 十九岁纪念|HBD To ME

    过了20年,终于摆脱了令人讨厌的应试生活.19岁,一半是高三,一半是大学,由高考,分成两半.说实话,我觉得大学也没有那么令人向往,换种方式读高四吧.长大了,对时间也没有什么概念了.要不是19岁在我的家 ...

  3. 一键部署nfs、rsync、sersync

    一键部署nfs.rsync.sersync 项目代码: 链接:https://pan.baidu.com/s/13I0BBAYsdK-KmPekZ5VpdA 提取码:u2tw --来自百度网盘超级会员 ...

  4. Activiti01-基本介绍

    1.工作流的定义 工作流是将一组任务组织起来以完成某个有序的过程:定义了任务的触发顺序和触发条件,而且每个任务可以由一个或多个软件系统完成,也可以由一个或一组人完成, 还可以由一个或多个人与软件系统协 ...

  5. Jpbc哈希函数如何实现

    1.(0,1)→Element元素 在Jpbc库中存在两个方法 Element A=G1.newRandomElement();A.setFromBytes(arr,0,arr.length);//A ...

  6. 基于ROS的串口底层写法

    serial_device.cpp #include "serial_device.h" namespace roborts_sdk { SerialDevice::SerialD ...

  7. Centos7下areaDetector IOC的编译后记

    https://github.com/EPICS-synApps/support/releases synApps出新版了,本文发文时的最新版为:R6-2 Centos7下areaDetector I ...

  8. CF1311F Moving Points

    题目传送门 思路 给出一种不需要脑子的四颗树状数组解法. 这四颗树状数组分别为:一颗维护负数,一颗维护负数个数,一颗维护正数,一颗维护正数个数. 首先考虑没有速度该怎么求. 不妨先按 \(x_i\) ...

  9. RabbitMQ基础和解疑

    一.基础概念 1. Producer:生产者,就是投递消息的一方 消息一般可以包含2个部分:消息体和标签(Label).消息体也可以称之为payload,在实际应用中,消息体一般是一个带有业务逻辑结构 ...

  10. 【ACR2015】持续临床缓解的RA患者能停用TNF拮抗剂吗?

    标签: 类风湿关节炎; 药物减停; 生物制剂; 减停后复发 荷兰一项随机对照试验显示, 已获持续6个月临床缓解或低疾病活动度的RA患者在停用TNFi的6个月内约三分之一病情复发 Moghadam HG ...