websocket python3 poll实现_Python实现websocket

一、 websocket概要:

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

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

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

2) 采用long poll的方式, 服务端不给客户端反馈, 客户端就一直等待, 服务就一直被挂起, 此阶段一直是阻塞状态, 而当服务器完成升级( http–>websocket )后, 上面两个问题就得到解决了:

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

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

二、 websocket通信过程

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

websocket python3 poll实现_Python实现websocket_第1张图片

websocket python3 poll实现_Python实现websocket_第2张图片

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”)

完整客户端代码如下:

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”, “

”+message.data+”

”);

}

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()

}

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 ) 一般也不会使用

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,python3,poll实现)