最近由于一个项目需要,写了一个简易的websocket服务端程序,其间也参考了网上的很多资料,我将用接下来的几个篇幅说明是怎么实现的,及遇到的一系列埂。

参考 (包括且不限于如下地址)

涉及到的模块

socket:socket通讯如侦听端口接收数据、发送数据等部分需要

struct:对发送和接收的数据包进行解包、打包等

hashlib,base64:通过接收浏览器的key生成websocket会话所需要的token

threading:实现多线程

time:对时间的处理

主要涉及到的函数

get_datalength:此函数在建立websocket会话,接受用户发送的数据后调用,通过解包接收到的bytes信息计算出用户发送过来的数据总长度及数据帧头部的大小。websocket帧在封装不同长度的内容时头部大小是不一样的,需要此函数处理后计算出所有数据是否接收完毕。

parser_data:浏览器在建立websocket会话后发送过来的bytes数据是有掩码加密的,此函数在建立websocket会话接受完用户发送的所有数据后调用,提取出用户发送过来的实际内容。

sendMessage:在建立websocket会话后,服务端通过socket通道发送数据到浏览器端时调用此函数,主要作用是在实际数据包头部增加websocket数据特有的帧。

定义到的类

WebSocketServer : WebSocket服务器对象,调用此类的begin方法后将开启服务端程序。

WebSocket:threading.Thread类的子类,处理每一个连接请求。

服务端代码

# coding: utf-8

import socket

import struct

import hashlib,base64

import threading

import time

connectionlist = {} #存放链接客户fd,元组

g_code_length = 0

g_header_length = 0 #websocket数据头部长度

PRINT_FLAG = True

"""

经测试发现IE 11浏览器在成功建立websocket连接后,会间隔30s发送空信息给服务器以验证是否处于连接状态,

因此服务区需要对收到的数据进行解码并判断其中载荷内容是否为空,如为空,应不进行广播

"""

# 计算web端提交的数据长度并返回

def get_datalength(msg):

global g_code_length

global g_header_length

g_code_length = msg[1] & 127

if g_code_length == 126:

g_code_length = struct.unpack('>H', msg[2:4])[0]

g_header_length = 8

elif g_code_length == 127:

g_code_length = struct.unpack('>Q', msg[2:10])[0]

g_header_length = 14

else:

g_header_length = 6

g_code_length = int(g_code_length)

return g_code_length

# 解析web端提交的bytes信息,返回str信息(可以解析中文信息)

def parse_data(msg):

global g_code_length

g_code_length = msg[1] & 127

if g_code_length == 126:

g_code_length = struct.unpack('>H', msg[2:4])[0]

masks = msg[4:8]

data = msg[8:]

elif g_code_length == 127:

g_code_length = struct.unpack('>Q', msg[2:10])[0]

masks = msg[10:14]

data = msg[14:]

else:

masks = msg[2:6]

data = msg[6:]

en_bytes = b""

cn_bytes = []

for i, d in enumerate(data):

nv = chr(d ^ masks[i%4])

nv_bytes = nv.encode()

nv_len = len(nv_bytes)

if nv_len == 1:

en_bytes += nv_bytes

else:

en_bytes += b'%s'

cn_bytes.append(ord(nv_bytes.decode()))

if len(cn_bytes) > 2:

cn_str = ""

clen = len(cn_bytes)

count = int(clen / 3)

for x in range(count):

i = x * 3

b = bytes([cn_bytes[i], cn_bytes[i + 1], cn_bytes[i + 2]])

cn_str += b.decode()

new = en_bytes.replace(b'%s%s%s', b'%s')

new = new.decode()

res = (new % tuple(list(cn_str)))

else:

res = en_bytes.decode()

return res

# 调用socket的send方法发送str信息给web端

def sendMessage(msg):

global connectionlist

send_msg = b"" #使用bytes格式,避免后面拼接的时候出现异常

send_msg += b"\x81"

back_str = []

back_str.append('\x81')

data_length = len(msg.encode()) #可能有中文内容传入,因此计算长度的时候需要转为bytes信息

if PRINT_FLAG:

print("INFO: send message is %s and len is %d" % (msg, len(msg.encode('utf-8'))))

# 数据长度的三种情况

if data_length <= 125:#当消息内容长度小于等于125时,数据帧的第二个字节0xxxxxxx 低7位直接标示消息内容的长度

send_msg += str.encode(chr(data_length))

elif data_length <= 65535:#当消息内容长度需要两个字节来表示时,此字节低7位取值为126,由后两个字节标示信息内容的长度

send_msg += struct.pack('b', 126)

send_msg += struct.pack('>h', data_length)

elif data_length <= (2^64-1):#当消息内容长度需要把个字节来表示时,此字节低7位取值为127,由后8个字节标示信息内容的长度

send_msg += struct.pack('b', 127)

send_msg += struct.pack('>q', data_length)

else:

print (u'太长了')

send_message = send_msg + msg.encode('utf-8')

for connection in connectionlist.values():

if send_message != None and len(send_message) > 0:

connection.send(send_message)

#删除连接,从集合中删除连接对象item

def deleteconnection(item):

global connectionlist

del connectionlist['connection'+item]

#定义WebSocket对象(基于线程对象)

class WebSocket(threading.Thread):

def __init__(self,conn,index,name,remote, path=""):

#初始化线程

threading.Thread.__init__(self)

#初始化数据,全部存储到自己的数据结构中self

self.conn = conn

self.index = index

self.name = name

self.remote = remote

self.path = path

self.GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

self.buffer = ""

self.buffer_utf8 = b""

self.length_buffer = 0

def generate_token(self, WebSocketKey):

WebSocketKey = WebSocketKey + self.GUID

Ser_WebSocketKey = hashlib.sha1(WebSocketKey.encode(encoding='utf-8')).digest()

WebSocketToken = base64.b64encode(Ser_WebSocketKey) # 返回的是一个bytes对象

return WebSocketToken.decode('utf-8')

#运行线程

def run(self):

#Log输出,套接字index启动

if PRINT_FLAG:

print('Socket %s Start!' % self.index)

global g_code_length

global g_header_length

self.handshaken = False #Socket是否握手的标志,初始化为false

while True:

if self.handshaken == False: #如果没有进行握手

if PRINT_FLAG:

print('INFO: Socket %s Start Handshaken with %s!' % (self.index,self.remote))

self.buffer = self.conn.recv(1024).decode('utf-8') #socket会话收到的只能是utf-8编码的信息,将接收到的bytes数据,通过utf-8编码方式解码为unicode编码进行处理

if PRINT_FLAG:

print("INFO: Socket %s self.buffer is {%s}" % (self.index, self.buffer))

if self.buffer.find('\r\n\r\n') != -1:

headers = {}

header, data = self.buffer.split('\r\n\r\n', 1) #按照这种标志分割一次,结果为:header data

#对header进行分割后,取出后面的n-1个部分

for line in header.split("\r\n")[1:]: #再对header 和 data部分进行单独的解析

key, value = line.split(": ", 1) #逐行的解析Request Header信息(Key,Value)

headers[key] = value

try:

WebSocketKey = headers["Sec-WebSocket-Key"]

except KeyError:

print("Socket %s Handshaken Failed!" % (self.index))

deleteconnection(str(self.index))

self.conn.close()

break

WebSocketToken = self.generate_token(WebSocketKey)

headers["Location"] = ("ws://%s%s" %(headers["Host"], self.path))

#握手过程,服务器构建握手的信息,进行验证和匹配

#Upgrade: WebSocket 表示为一个特殊的http请求,请求目的为从http协议升级到websocket协议

handshake = "HTTP/1.1 101 Switching Protocols\r\n"\

"Connection: Upgrade\r\n"\

"Sec-WebSocket-Accept: " + WebSocketToken + "\r\n"\

"Upgrade: websocket\r\n\r\n"

self.conn.send(handshake.encode(encoding='utf-8')) # 前文以bytes类型接收,此处以bytes类型进行发送

# 此处需要增加代码判断是否成功建立连接

self.handshaken = True #socket连接成功建立之后修改握手标志

#向全部连接客户端集合发送消息,(环境套接字x的到来)

sendMessage("Welocomg " + self.name + " !")

g_code_length = 0

else:

print("Socket %s Error2!" % (self.index))

deleteconnection(str(self.index))

self.conn.close()

break

else:

# 每次接收128字节数据,需要判断是否接收完所有数据,如没有接收完,需要循环接收完再处理

mm = self.conn.recv(128)

#计算接受的长度,判断是否接收完,如未接受完需要继续接收

if g_code_length == 0:

get_datalength(mm) # 调用此函数可以计算并修改全局变量g_code_length和g_header_length的值

self.length_buffer += len(mm)

self.buffer_utf8 += mm

if self.length_buffer - g_header_length < g_code_length:

if PRINT_FLAG:

print("INFO: 数据未接收完,接续接受")

continue

else:

if PRINT_FLAG:

print("g_code_length:", g_code_length)

print("INFO Line 204: Recv信息 %s,长度为 %d:" % (self.buffer_utf8, len(self.buffer_utf8)))

if not self.buffer_utf8:

continue

recv_message = parse_data(self.buffer_utf8)

if recv_message == "quit":

print("Socket %s Logout!" % (self.index))

nowTime = time.strftime('%H:%M:%S',time.localtime(time.time()))

sendMessage("%s %s say: %s" % (nowTime, self.remote, self.name+" Logout"))

deleteconnection(str(self.index))

self.conn.close()

break

else:

nowTime = time.strftime('%H:%M:%S',time.localtime(time.time()))

sendMessage("%s %s say: %s" % (nowTime, self.remote, recv_message))

g_code_length = 0

self.length_buffer = 0

self.buffer_utf8 = b""

#WebSocket服务器对象()

class WebSocketServer(object):

def __init__(self):

self.socket = None

self.i = 0

#开启操作

def begin(self):

if PRINT_FLAG:

print('WebSocketServer Start!')

self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

ip = '10.1.80.83'

port = 8080

if PRINT_FLAG:

print("WebServer is listening %s,%d" % (ip,port))

self.socket.bind((ip,port))

self.socket.listen(50)

#全局连接集合

global connectionlist

while True:

#服务器响应请求,返回连接客户的信息(连接fd,客户地址)

connection, address = self.socket.accept()

#根据连接的客户信息,创建WebSocket对象(本质为一个线程)

#sockfd,index,用户名,地址

newSocket = WebSocket(connection,self.i,address[0],address)

#线程启动

newSocket.start()

#更新连接的集合(hash表的对应关系)-name->sockfd

connectionlist['connection'+str(self.i)]=connection

self.i += 1

if __name__ == "__main__":

server = WebSocketServer()

server.begin()

html页面代码

WebSocket

html,body{font:normal 0.9em arial,helvetica;}

#log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}

#msg {width:440px;}

var socket;

function init(){

var host = "ws://10.1.80.83:8080/";

try{

socket = new WebSocket(host);

socket.onopen = function(msg){

console.log("socket session create sucess!");

log("socket session create sucess!");

};

socket.onmessage = function(msg){

console.log("message Sucess");

//socket.send("Hello");

//log(msg.data);

log(msg.data)

};

socket.onclose = function(msg){ console.log("close Sucess"); log("Connection Lose!"); };

socket.onerror = function(msg){ console.log("Error!"); };

}catch(ex){

log(ex);

}

$("msg").focus();

}

function send(){

var txt,msg;

txt = $("msg");

msg = txt.value;

console.log("message is", msg)

if(!msg){

alert("Message can not be empty");

return;

}

txt.value="";

txt.focus();

try{

//log(msg);

socket.send(msg);

}catch(ex){

log(ex);

}

}

window.οnbefοreunlοad=function(){

try{

socket.send('quit');

socket.close();

socket=null;

}catch(ex){

log(ex);

}

};

function $(id){

return document.getElementById(id);

}

function log(msg){

$("log").innerHTML+="
"+msg;

}

function onkey(event){

if(event.keyCode==13){

send();

}

}

WebSocket

发送

python 3.5.2页面_Python 3.5.2实现websocket服务端相关推荐

  1. python websocket server模块_Python 3.5.2实现websocket服务端(四): WebSocketServer类实现...

    #删除连接,从集合中删除连接对象item def deleteconnection(item): global connectionlist del connectionlist['connectio ...

  2. python服务端编程_Python WebSocket服务端编程代码完成gtalk机器人

    本文python源码为实现,Python WebSocket服务端编程代码完成gtalk机器人的全部代码段.需要用到python sys.sleekxmpp.reactor等python模块及方法,在 ...

  3. 【Python】基于 Flask 框架,模拟微信朋友圈的服务端

    [Python]基于 Flask 框架,模拟微信朋友圈的服务端 一.目的:模拟微信朋友圈的服务端,需要满足以下场景: 1.发表内容时带上图片信息,内容包括(具体内容和id,id指定全局唯一标识),客户 ...

  4. python实现websocket服务端

    2019独角兽企业重金招聘Python工程师标准>>> ws.py是服务端,文件内容如下 #!/usr/bin/env python import socket import bas ...

  5. python requests发送websocket_Pywss - 用python实现WebSocket服务端

    一种类似Flask开发的WebSocket-Server服务端框架,适用python3.X 1.安装模块Pywss pip install pywss 2.搭建简易服务器 2.1 服务端代码 代码简介 ...

  6. php和asp渲染页面,Vue.js与 ASP.NET Core 服务端渲染功能

    在前端使用 Vue.js,Vue 服务端渲染直到第二个版本才被支持. 在本例中,我想展示如何将 Vue.js 服务端渲染功能整合 ASP.NET Core. 我们在服务端使用了 Microsoft.A ...

  7. python 实现简单查询页面_python web 实现简易天气查询

    本文记录使用 Flask 快速完成一个 简易天气查询 MVP 程序的探索,学习过程.程序采用Flask+Jinja2实现,其中Flask扩展包括:1.flask-bootstrap,2.Flask-W ...

  8. python多线程实现访问页面_Python实现多线程爬虫

    最近在写爬虫程序爬取亚马逊上的评论信息,因此也自学了很多爬虫相关的知识,其实网络上已经有很多基于Python的入门爬虫程序了,所以学习起来比较方便,唯独那个多线程爬虫一直都学的不是很明白,所以就写下这 ...

  9. python多线程实现访问页面_python 多线程实现网页自动截图

    准备工作 安装selenium 2.48.0,一定不要安装最新版本的,最新版本不支持phantomjs. 用phantomjs是因为它是单文件版.下载地址:https://phantomjs.org/ ...

最新文章

  1. 伪元素first-letter
  2. SAP PM 初级系列3 - 主数据相关的基础设置
  3. 我们在开源项目中是怎样埋彩蛋的
  4. (转)HTTP 协议之压缩
  5. 逃出你的肖申克(四):理智与情感
  6. 什么是Session?
  7. (2) GoJS Node简介
  8. 还在使用集合类完成这些功能?不妨来看看 Guava 集合类!!!
  9. PCIe device tree range属性详解
  10. android的findviewbyid,Android开发中如何简化findViewById类型转换
  11. Package php5 have no installation candidate解决方案
  12. iframe如何发送请求_如何实现高性能的在线 PDF 预览
  13. 基于javaweb+jsp的员工薪资工资管理系统(JavaWeb JSP MySQL Servlet SSM SpringBoot Layui Ajax)
  14. 银行家算法(C++实现)
  15. 炫龙笔记本怎么进bios设置u盘启动图文教程
  16. fastjson转换json字符串key的首字母小写变大写的解决办法
  17. iOS开发:如何使用ShareSDK让APP快速拥有分享功能
  18. php无法导出excel,PHPExcel导出Excel文件时出现错误的解决办法
  19. 欢迎百合网联合创始人慕岩,追梦人创服李圆峰莅临龙测科技投资考察
  20. 前端学习——HTML(二) 列表

热门文章

  1. vs2010 学习Silverlight学习笔记(8):使用用户控件
  2. System.Net.Mail的属性与方法集锦
  3. 【剑指offer】面试题30:包含min函数的栈
  4. 基本的Windows相关的DOS命令
  5. 公众号 接收规则 消息_微信公众平台 发送模板消息(Java接口开发)
  6. python生成文件夹并向文件夹写文件_python - 文件练习生成100个MAC地址写入文件
  7. TensorFlow 中三种启动图用法
  8. 十个不可不看的Matlab GUI
  9. 数字图像处理 第二章 图像处理基础
  10. Android 逆向分析大全