一、websocket概要:

  websocket是基于TCP传输层协议实现的一种标准协议(关于网络协议,可以看看文末的图片),用于在客户端和服务端双向传输数据

  传统的客户端想要知道服务端处理进度有两个途径:

  1)通过ajax不断轮询,由于http的无状态性,每次轮询服务器都需要去解析http协议,对服务器压力也很大

  2)采用long poll的方式,服务端不给客户端反馈,客户端就一直等待,服务就一直被挂起,此阶段一直是阻塞状态

  而当服务器完成升级(http-->websocket)后,上面两个问题就得到解决了:

    1)被动性,升级后,服务端可以主动推送消息给客户端,解决了轮询造成的同步延迟问题

    2)升级后,websocket只需要一次http握手,服务端就能一直与客户端保持通信,直到关闭连接,这样就解决了服务器需要反复解析http协议,减少了资源的开销

二、websocket通信过程:

  websocket目前基本主流浏览器都已经支持,IE10以下不支持。

  1、建立连接

    在客户端,new WebSocket实例化一个新的WebSocket客户端对象,连接类似 ws://yourdomain:port/path 的服务端 WebSocket URL, WebSocket 客户端对象会自动解析并识别为 WebSocket 请求,从而连接服务端接口,执行双方握手过程,客户端发送数据格式类似:

    1)客户端请求报文:

GET / HTTP/1.1

Upgrade:websocket  #line1

Connection:Upgrade  #line2 :与http请求报文比,多了line1和line2这两行,它告诉服务器此次发起的是websocket协议,而不是http协议了,记得要升级哦

Host:example.com

Origin:http://example.com

Sec-WebSocket-Key:sN9cRrP/n9NdMgdcy2VJFQ==  # line3:这个是浏览器随机生成的一个base64加密值,提供基本的防护,告诉服务器,我有提供的密码的,我会做验证的,防止恶意或无意的连接
  
Sec-WebSocket-Version:13  #line4 :告诉服务器使用的websocket版本,如果服务器不支持该版本,会返回一个Sec-WebSocket-Versionheader,里面包含服务器支持的版本号

    客户端创建websocket连接

      var ws = new websocket("ws:127.0.0.1:8000")

    完整客户端代码如下:

<script type="text/javascript">
var ws;
var box = document.getElementById("box"); function startWS(){
ws = new websocket("ws:127.0.0.1:8000");
ws.onopen = function(msg){
console.log("websocket opened!");
}
ws.onomessage = function(message){
console.log("receive message:"+message.data);
box.insertAdjacentHTML("beforeend", "<p>"+message.data+"</p>");
}
ws.onerror = function(err){
console.log("error:"+err.name+err.number);
}
ws.onclose = function(){
console.log("websocket closed!")
}
} function sendMsg(){
console.log("sending a message...");
var text = document.getElementById("text");
ws.send(text.value);
} window.onbeforeunload = function(){
ws.onclose = function(){}
ws.close()
}
</script>

    2)服务端响应报文:

HTTP/1.1 101 Switching Protocols  # 101表示服务端已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求

Upgrade:websocket

Connection:Upgrade  # 这里两行是告诉浏览器,我已经成功切换协议了,协议是websocket

Sec-WebSocket-Accept:HSmrc0sM1YUkAGmm50PpG2HaGwK=  #经过服务器确认并加密后的Sec-WebSocket-Key

Sec-WebSocket-Protocol:chat  # 表示最终使用的协议,至此http就已经完成全部的工作,接下来就是完全按照websocket协议进行了。

     上文的Sec-WebSocket-Accept加密算法:

        a)将Sec-WebSocket-Key和258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接

        b)通过SHA1计算出摘要,并转成Base64字符串

          如token = base64.b64encode(hashlib.sha1(key+magic_str).encode("utf8").degist())

          这里在做加密之前,key一定要记得看前后有没有空白,有的话要记得去空白,不然加密的结果会一直报错不匹配,这个坑被坑了很久

        注意:此处的Sec-WebSocket-Key/Sec-WebSocket-Accept的换算,只能带来基本保障,但连接是否安全,数据是否安全,客户端 服务端是否是合法的ws客户端 ws服务端,并没有实际保证

      Sec-WebSocket-Protocol:表示最终使用的协议

完整的服务端代码:

  1)创建websocket服务端

import socket
import threading global clients
clients = {} class Websocket_Server(threading.Thread):
def __init__(self, port):
self.port = port
super(Websocket_Server, self).__init__() 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) while(True):
       # 等待客户端连接
conn, addr = sock.accept()
print("客户端{}连接成功:".format(addr))
conn.send(("welcome...").encode("utf8"))
while(True):
try:
info = conn.recv(1024)
connId = "ID:"+str(addr[1])
clients[connId] = conn print("{0}:{1}".format(connId, info.decode("utf8"))) except Exception as e:
print(e) msg = input()
conn.send(msg.encode("utf8")) if info == b"bye":
print("客户端退出")
conn.close()
break

上面创建了websocket服务端,通过socket.socket()创建了TCP服务对象

    接收两个参数:family和type

      family:有三种:

        AF_INET :即IPV4(默认)

        AF_INET6:即IPV6

        AF_UNIX:只能够用于单一的Unix系统进程间通信

      type:套接字类型:

        流套接字(SOCK_STREAM)(默认):只读取TCP协议的数据,用于提供面向连接,可靠的数据传输服务。该服务可以保证数据可以实现无差错无重复发送,并按序接收。之所以能够实现可靠的数据传输,原因在于使用了传输控制协议(TCP)

        数据报套接字(SOCK_DGRAM):只读取UDP协议的数据。提供了一种无连接服务,该服务并不能保证数据的可靠性。有可能在数据传输过程中出现数据丢失,错乱重复等。由于数据包套接字不能保证数据的可靠性,对于有可能出现数据丢失的情况,在程序中要做相应的处理。

        原始套接字(SOCK_RAW):原始套接字和标准套接字(上面两种)的区别是:原始套接字可以读取内核没有处理的IP数据包。而流套接字只能读取TCP协议的数据;数据包套接字只能读取UDP协议的数据

        可靠UDP形式:(SOCK_RDM),会对数据进行校验,一般不会使用

        可靠的连续数据包:(SOCK_SEQPACKET)一般也不会使用

  2)创建websocket客户端

import socket
import threading class Websocket_Client(threading.Thread):
def __init__(self):
self.host ="localhost"
self.port = 8000
self.address = (host, port)
self.buffer = 1024 def run():
#创建TCP客户端程序
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务端
tcp_client.connect(self.address)
while True:
info = tcp_client.recv(self.buffer)
print("{}".format(str(info, encoding="utf8"))) msg = input()
tcp_client.send(msg.encode("utf8"))
if info.lower().decode("utf8")=="bye":
tcp_client.close()
break

完成上述代码,分别打开两个终端,运行WebSocket_Server和WebSocket_Client代码,控制台交互结果如下,可以看到已经成功实现了客户端和服务端的通信:

  3)如何监听浏览发送过来的socket连接请求?修改上面的WebSocket_Client如下:

class WebSocket_Client():
  def __init__(self, conn, connId):
    self.connection = conn
    self.connId= connId
    super(WebSocket_Client, self).__init__()
  
  def run():
    print("new socket joined")
    # 前端传递过来的请求连接数据
    data = self.connection.recv(1024)
    try:
      headers = self.parse_header(data)
      token = self.generate_token(headers["Sec-WebSocket-Key"]).decode("utf8")
      response ="HTTP/1.1 101 Switching Protocols\r\n" \
            "Connection:Upgrade\r\n" \
            "Upgrade:websocket\r\n" \
            "Sec-WebSocket-Accept:{0}\r\n" \
            "WebSocket-Protocol:chat\r\n\r\n".format(token)
      # 通知前端已经成功建立连接
      self.connection.send(response.encode("utf8"))
    except socket.error as e:
      print("unexpect error:", e)
      clients.pop(self.connId)
    while True:
      
# 启动 Socket 并监听连接,使用socket中的同名方法,创建TCP服务对象
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # socket.AF_INET是指使用哪种IP地址,socket.AF_INET即使用IPv4,如果是socket.AF_INET6的话,则使用IPv6;socket.SOCKET_STREAM是指使用的套接字类型,在这里我们使用流式套接字。
try:
sock.bind(("127.0.0.1",8000))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.listen(5)
except Exception as e:
logging.error(e)
return
else:
logging.info(Server running...)   # 等待访问
while True:
conn, addr = sock.accept() #此时会进入waiting状态
data = str(conn.recv(1024))
logging.debug(data) header_dict = {}
header, _ = data.split(r"\r\n\r\n", 1)
for line in header.split(r"\r\n")[1:]:
key, val = line.split(":",1)
header_dict[key] = val if "Sec-WebSocket-Key" not in header_dict:
logging.error(this socket is not websocket, connection closed...)
conn.close()
return magic_key = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
sec_key = header_dic["Sec-WebSocket-Key"]+magic_key
key = base64.b64encode(hashlib.sha1(bytes(sec_key, encoding="utf-8")).digest())
key_str = str(key)[2:30]
logging.debug(key_str) response = "HTTP/1.1 101 Switching Protocols\r\n"\
"Connection:Upgrade\r\n"\
"Upgrade:websocket\r\n"\
"Sec-WebSocket-Accept:{0}\r\n"\
"WebSocket-Protocol:chat\r\n\r\n".format(key_str)
conn.send(bytes(response),encoding="utf-8")
logging.debug("send the handshake data")
WebsocketThread(conn).start()

    3)进行通信:

      Sever端接收到client发来的报文进行解析

      

下面是软件通信的七层结构:

  下三层结构偏向于数据通信,上三层更偏向于数据处理,中间的传输层是上三层与下三层之间的连接桥梁,每一层做不同的工作,上层协议依赖于下层协议,上层协议需要调用下层协议的接口,下层协议使用上层协议的参数,分工合作。

  socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。属于传输控制层协议,websocket是一个典型的应用层协议。

参考资料:https://www.cnblogs.com/JetpropelledSnake/p/9033064.html

    https://www.cnblogs.com/lichmama/p/3931212.html

python 实现 websocket的更多相关文章

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

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

  2. python 实现websocket

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

  3. HTTPS请求HTTP接口被浏览器阻塞,python实现websocket客户端,websocket服务器,跨域问题,dwebsocket,https,拦截,服务端

    HTTPS请求HTTP接口被浏览器阻塞,python实现websocket客户端,websocket服务器,跨域问题,dwebsocket,https,拦截,服务端 发表时间:2020-03-05 1 ...

  4. 利用python对websocket进行并发压测

    简述 产品经理鉴于运营反馈并对程序的websocket长连接保持怀疑的态度,让我对websocket服务器进行压力测试,我内心是拒绝的. 开发思路 查阅websocket的相关资料,查到python的 ...

  5. Python基于websocket实时通信的实现—GoEasy

    Python websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1.        获取GoEasy appkey. 在goeasy官网上注册一个 ...

  6. python tornado websocket 多聊天室(返回消息给部分连接者)

    python tornado 构建多个聊天室, 多个聊天室之间相互独立, 实现服务器端将消息返回给相应的部分客户端! chatHome.py // 服务器端, 渲染主页 --> 聊天室建立web ...

  7. 基于python的websocket开发,tomcat日志web页面实时打印监控案例

    web socket 接收器:webSocket.py 相关依赖 # pip install bottle gevent gevent-websocket argparse from bottle i ...

  8. 用python实现websocket请求遇到的问题及解决方法。

    想要实现python的ws库功能,实时获取对方服务器ws协议返回的数据,查了下百度,用如下流程: ws = create_connection("wss://ws.xxxxxxx.info/ ...

  9. python 版websocket实现

    ubuntu下python2.76 windows python 2.79, chrome37 firefox35通过 代码是在别人(cddn有人提问)基础上改的, 主要改动了parsedata和se ...

  10. python模拟websocket握手过程中计算sec-websocket-accept

    背景 以前,很多网站使用轮询实现推送技术.轮询是在特定的的时间间隔(比如1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给浏览器.轮询的缺点很明显,浏览器需要不断的向服 ...

随机推荐

  1. 讲座 - Transposable elements, non-coding RNAs and epigenetic control in embryonic stem cells

    Dr. Andrew Paul HutchinsDepartment of BiologySouthern University of Science and Technology, Shenzhen ...

  2. The Art of Picking Intel Registers Intel寄存器的艺术

    https://www.swansontec.com/sregisters.html I wrote this article for an online magazine called Scene ...

  3. TreeFrog Framework : High-speed C++ MVC Framework for Web Application http://www.treefrogframework.org

    TreeFrog Framework : High-speed C++ MVC Framework for Web Application http://www.treefrogframework.o ...

  4. java(集合框架)(转)

    前言 集合①只能存放对象,存放基本类型会自动转成对应的对象②可以存放不同类型的对象(如果不使用泛型的话),且不限数量③集合中存放的只是对象的引用 集合详解 集合-1.png 集合-2.png   It ...

  5. FrameLayout--霓虹灯

    主函数里面,新建一个线程,控制改变背景色 package com.example.framelayout import android.support.v7.app.AppCompatActivity ...

  6. datax实例——全量、增量同步

    一.全量同步 本文以mysql -> mysql为示例: 本次测试的表为mysql的系统库-sakila中的actor表,由于不支持目的端自动建表,此处预先建立目的表: CREATE TABLE ...

  7. 123457123456#0#----com.DoraGame.AiMiYu20--前拼后广--caimi-doraX

    com.DoraGame.AiMiYu20--前拼后广--caimi-doraX

  8. AD域策略启动关机脚本不执行的注意事项

    其实主要是脚本路径的问题. 错误一: 直接使用右侧的添加按钮,添加了预控的本地路径.如上图第二行. 错误二: 直接使用右侧的添加按钮,添加了脚本的网络路径,如上图第三行. 正确的方法: 点击下方的显示 ...

  9. SpringBoot学习笔记:动态数据源切换

    SpringBoot学习笔记:动态数据源切换 数据源 Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法.通常,DataSource使用URL和一些凭据来建立数据 ...

  10. iOS-UIDocumentInteractionController打开和预览文档

    iOS提供了使用其他app预览文件的支持,这就是Document Interaction Controller.此外,iOS也支持文件关联,允许其他程序调用你的app打开某种文件.而且,从4.2开始, ...