python 3.5.2页面_Python 3.5.2实现websocket服务端

最近由于一个项目需要,写了一个简易的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

发送

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/502898.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java连接sqlserver 的sqlhelper类_SQLserver数据库操作帮助类SqlHelper

using System;using System.Data;using System.Xml;using System.Data.SqlClient;using System.Collections;namespace SQL.Access{/// /// SqlServer数据访问帮助类/// public sealed class SqlHelper{#region 私有构造函数和方法private SqlHelper() {}/// /// 将SqlParamete…

python编写抢座位软件_程序员硬核Python抢票教程”,帮你抢回家车票

盼望着&#xff0c;盼望着&#xff0c;春节的脚步近了&#xff0c;然而&#xff0c;每年到这个时候&#xff0c;最难的&#xff0c;莫过于一张回家的火车票。据悉&#xff0c;今年春运期间&#xff0c;全国铁路发送旅客人次同比将增长8.0%&#xff0c;达到4.4亿人次&#xff0c…

java io 缓存读取_Java 文件IO写入读取四种方法

第一种&#xff1a;字节流 FileInputStream FileOutputStream1.1 读取操作//先创建一个和硬盘连接的流(打通硬盘和内存的通道)FileInputStream fis new FileInputStream("D:\\Demo.txt");//创建缓存区大小是1kbyte[] bytes new byte[1024];int data 0; //存储有效…

python集合可以修改吗_修改包含Python3中的集合的集合列表-问答-阿里云开发者社区-阿里云...

我试图创建一个以元组为元素的列表。每个元组都有4个整数。前两个整数是对2个range进行压缩的结果&#xff0c;而其他2个则是对2个不同的整数进行压缩的结果。我正在使用此代码创建元组和最终列表&#xff0c;这些列表是从笛卡尔乘积派生的&#xff0c;如下所示&#xff1a;获取…

hive mysql性能_Hive数据库安全审计功能

【Hive数据库安全审计简介】Hive数据库安全审计是一款基于数据库通讯协议准确分析和SQL完全解析技术的数据库安全审计系统。实现了对数据库操作、访问用户及外部应用用户的审计&#xff0c;可以用于安全合规、用户行为分析、运维监控、风控审计、事件追溯等与数据库安全相关的管…

linux安装sz rz_超级好用的文件传输命令rz与sz

做生物信息经常需要在本地客户端与服务器之间进行文件的传输&#xff0c;例如将要分析的数据传到Linux服务器上&#xff0c;进行分析&#xff0c;分析结束之后将结果下载到本地windows系统进行查看。以前我们都推荐大家使用比较稳定&#xff0c;并且支持断点续传的Filezilla或者…

java mongodb gridfs_查询MongoDB GridFS元数据(Java)

我想要做的是通过查询元数据的字段来获取GridFS文件列表.例如,我得到一个GridFS文件文件,如下所示&#xff1a;{ "_id" : { "$oid" : "4f95475f5ef4fb269dbac954"} , "chunkSize" : 262144 , "length" : 3077 , "md5&q…

php 防止跨站脚本攻击,php防止sql注入以及xss跨站脚本攻击

1.post数据封装转义函数 防sql注入 eag&#xff1a;addslashes($username);​addslashes($password);​eag:防止sql注入函数封装function deepslashes($data){#判断$data的表现形式 并且需要处理空的情况if(empty($data)){return($data);}​#高级简写 return is_array($data) …

visual studio 按钮判断管理员和用户_用户管理的设计原则

后台的使用者通常是企业的各类权限的管理员&#xff0c;通过对用户管理的合理设计&#xff0c;让管理员准确、高效地完成既定任务&#xff0c;来实现用户管理的价值。后台产品的使命是为企业提升效率&#xff0c;降低成本&#xff0c;而用户管理是后台的核心部分&#xff0c;用…

php 随机颜色,php生成随机颜色的代码实例

PHP生成随机颜色的代码实例&#xff1a;方法一&#xff1a;function color_txt($str){if ( is_array($str) ){$str $str[1];}$len mb_strlen($str);$colorTxt ;for($i0; $i$colorTxt . .mb_substr($str,$i,1,utf-8).;}return $colorTxt;}function rand_color(){re…

windows。forms.timer设置第一次不等待_面试官:换人!他连 TCP 这几个参数都不懂(一)...

前言TCP 性能的提升不仅考察 TCP 的理论知识&#xff0c;还考察了对于操作系统提供的内核参数的理解与应用。TCP 协议是由操作系统实现&#xff0c;所以操作系统提供了不少调节 TCP 的参数。如何正确有效的使用这些参数&#xff0c;来提高 TCP 性能是一个不那么简单事情。我们需…

php 输出可以设置格式文件,php格式输出文件var_export函数实例

本文实例讲述了php格式输出文件var_export函数的用法。分享给大家供大家参考。具体如下&#xff1a;var_export:php 4 > 4.2.0, php 5var_export -- 输出或返回一个变量的字符串表示.描述:mixed var_export (mixed expression [,bool return])此函数返回关于传递给该函数的变…

python tornado websocket_基于Python Tornado框架的websocket服务

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。Tornado中定义了tornado.websocket.WebSocketHandler来处理websocket请求。Tornado框架的安装以及入门这里就不说了&#xff0c;想入门tornado的请移步我的另一篇文章奥小飞&#xff1a;基于Python的…

微信网页开发教程 php,PHP实现微信网页授权开发教程,php授权教程_PHP教程

PHP实现微信网页授权开发教程&#xff0c;php授权教程微信网页授权是服务号才有的高级功能&#xff0c;开发者可以通过授权后获取用户的基本信息&#xff1b;在此之前&#xff0c;想要获取消息信息只能在用户和公众号交互时根据openid获取用户信息&#xff1b;而微信网页授权可…

lstm原文_LSTM模型与水文模型在径流模拟中的比较

学术简报题目:Comparison of Long Short Term Memory Networks and the Hydrological Model in Runoff Simulation作者:Hailin You1,Hongxiang Fan2,Ligang Xu2,Yongmi Wu1,*,Lizhen Liu2 and Zhong Yao1作者单位:1 Poyang Lake Research Center, Jiangxi Academy of Sciences,…

php 获取已用空间,PHP5:获取导入的名称空间列表 - php

是否可以在当前上下文中获取PHP文件中所有导入的类/命名空间的列表&#xff1f;例如&#xff1a;namespace A;use B, C\D;我想得到这个数组&#xff1a;array(B, C\D);原因是我正在构建一个Mapper注册表&#xff0c;并且希望能够在当前上下文中使用别名类名而不是全名来查询该M…

[英语单词] piss in the wind

有点粗俗的俚语&#xff1a;piss in the wind。 感觉这个谚语是写在实&#xff1a;就是找不准点&#xff0c;pointless&#xff1b;然后引申代表是浪费时间。 https://lkml.org/lkml/2006/10/10/130 This is not a pointless piss-in-the-wind discussion; unless we define ra…

pythonos模块介绍_Python OS模块介绍

OS模块简单的来说它是一个Python的系统编程的操作模块&#xff0c;可以处理文件和目录这些我们日常手动需要做的操作。可以查看OS模块的帮助文档&#xff1a;>>> import os #导入os模块>>> help(os) #查看os模块帮助文档&#xff0c;里面详细的模块相关函数…

php 安装 bzip2,php如何安装bzip2

php安装bzip2的方法&#xff1a;首先安装扩展依赖的系统bzip2开发库&#xff1b;然后编译安装php源码包目录下的“ext/bz2”扩展&#xff1b;最后修改“php.ini”配置文件即可。php安装bzip2安装扩展依赖的系统bzip2开发库sudo yum install bzip2-devel编译安装php源码包目录下…

python处理滑块验证码_使用python实现滑动验证码

首先安装一个需要用到的模块pip install social-auth-app-django安装完后在终端输入pip list会看到social-auth-app-django 3.1.0social-auth-core 3.0.0然后可以来我的github&#xff0c;下载关于滑动验证码的这个demo&#xff1a;https://github.com/Edward66/slide_auth_cod…