一、什么是 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. Python基本数据类型,用户交互,格式化输出,运算符,多种赋值方式,多种运算符

    Python基本数据类型,用户交互,格式化输出,运算符,多种赋值方式,多种运算符 一.Python基本数据类型 1.回顾之前学过的基本数据类型 1.整型(整数) 应用场景:年级,班级人数,年份 代码实 ...

  2. 如何又快又好实现 Catalog 系统搜索能力?火山引擎 DataLeap 这样做

      摘要 DataLeap 是火山引擎数智平台 VeDI 旗下的大数据研发治理套件产品,帮助用户快速完成数据集成.开发.运维.治理.资产.安全等全套数据中台建设,降低工作成本和数据维护成本.挖掘数据价 ...

  3. 【Oculus Interaction SDK】(五)设置不同的抓握手势

    前言 这篇文章是[Oculus Interaction SDK]系列的一部分,如果发现有对不上的对方,可以回去翻看我之前发布的文章,或在评论区留言.如果文章的内容已经不适用于新版本了,也可以直接联系我 ...

  4. SqlServer Express 自动备份数据库(没有sql代理服务的情况下)

    一.由于Express版本的SQLServer没有SQL代理服务(SQLSERVERAGENT),所以就不能通过SQLServer作业来进行自动备份了,那么我们可以用Windows计划任务来定时处理 ...

  5. Spring(Ioc DI、Spring的继承-依赖)

    IoC Di Di 指的是bean之间的依赖注入,设置对象之间的级联关系 Classes: package com.southwind.entity; import lombok.Data; @Dat ...

  6. 深度学习-RNN

    目录 I.前言 介绍RNN的概念和应用 II. RNN基础 RNN的概念和结构 RNN的前向传播和反向传播算法 前向传播算法 反向传播 RNN的变种:LSTM和GRU LSTM GRU III. RN ...

  7. 题解 P2602 [ZJOI2010] 数字计数

    虽然说是数位 dp 入门题但是还是不是很会(悲 看了题解才做出来,中途因为各种 SB 问题调了很长时间(悲 聪明的你一定能看出来这是数位 dp,因此令 \(i\) 为当前填的位数,\(limit\) ...

  8. 重要内置函数、常见内置函数(了解)、可迭代对象、迭代器对象、for循环原理、异常捕获

    目录 一.重要内置函数 二.常见内置函数(了解) 三.可迭代对象 四.迭代器对象 五.for循环内部原理 六.捕捉异常 一.重要内置函数 1. zip 说白了就是压缩几组数据值,说细了就是将可迭代对象 ...

  9. vs2019 代码片段管理

    工具/代码片段管理 上面地址是vs内部代码片段,可以参考写自定义片段 写好的模板保存为.snippet文件,放到固定文件夹中,然后使用添加,直接找到文件夹添加即可 <?xml version=& ...

  10. 公司有两台电脑,却分给一个上网ip

    解决办法,只限于本公司: 电脑a和电脑b 电脑a先用ip 6.21上网,之后将ip改为其它: 电脑b改为6.21 此时两台电脑都能上网了