websocket python3 poll实现_python实现的websocket总结 —— wspy

之前曾有php版的websocket封装包,见Websocket——php实战,最近使用python做一些功能,需要用到对websocket的操作,因此,参照之前的实现,实现了这个python版本。源码见https://github.com/OshynSong/wspy。

整体实现起来,需要在建立socket监听端口,这需要用到socket标准库模块;之后,需要对对网络字节流进行操作,这个方面python有struct标准库模块,这个非常好用;另外涉及到加密解密操作,还有hashlib模块和sha模块等使用。特别在此总结一下,目的主要是

1 备忘

2. 总结与思考

1 socket 操作

1 本地Socket建立

建立TCP服务器的一般流程:

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

sock.bind((addr,port))

sock.listen(10)

建立好本地socket,并绑定地址与端口进行监听。

2 并发连接策略

之后,需要使用不同的策略处理多个客户端连接的问题,最普通的处理方式就是直接使用accept阻塞,这样服务器端每次只能处理一个客户端连接。然后python标准库提供了select模块,里面有select、poll和epoll这些不同的并发连接的处理策略。其中poll和epoll只能在linux下使用,而且epoll在linux 2.6之后的版本才能使用。当然并发处理效果来看,epoll比poll性能更好,poll比select性能更优。但是select确可以在多种平台下使用,为了兼容Windows系统,本次实现中使用的是select策略,具体如下:

...#接上述socket建立代码

while True:

rs, ws, es = select.select([sock], [], [])

for r in rs:

if r is sock: #r 是服务器端socket

cliSock,addr = r.accept()

r.connect(cliSock) #建立于客户端连接

else:

try:

data = r.recv(bufferLen)

... #处理客户端连接发送的数据

...

poll方法也是select模块内的方法,使用起来比select更简单。首先使用poll建立一个poll对象,然后使用它的register方法注册一个文件描述符,unregister方法可以移除注册对象。之后可以调用poll方法得到(fd,event)格式的列表,fd是文件描述符,event代表发生的事件。event是一个位掩码,可以使用select模块的常量进行按位操作。

select模块中polling事件常量:

事件名

描述

POLLIN

读取来自文件描述符的数据

POLLPRI

读取来自文件描述符的紧急数据

POLLOUT

文件描述符的数据已准备好,可无阻塞写入

POLLERR

与文件描述符有关的错误情况

POLLHUP

挂起,连接丢失

POLLNVAL

无效请求,连接没有打开

下面是使用poll策略的示例代码:

...#接上述socket建立代码

fdmap = {sock.fileno() : s}

p = select.poll()

p.register(sock)

while True:

events = p.poll()

for fd,event in events:

if fd in fdmap: #本地socket

c,addr = sock.accept()

print ‘Connected from ‘, addr

p.register(c)

fdmap[c.fileno()] = c

elif event & select.POLLIN:

data = fdmap[fd].recv(buffer)

...#数据操作

elif event & select.POLLERR: #断开连接

p.unregister(fd)

del fdmap[fd]

......

2 Struct处理字节数据

这个标准库模块就是用来转换python的数据值与C风格的数据类型的交互,特别是二进制文件和网络的字节数据。主要的方法:

struct.pack(fmt, v1, v2…)

struct.pack_into(fmt, buffer, offset, v1, v2…) (将v1,v2等值按照fmt格式pack到buffer字符串以offset开始的之后的位置)

struct.unpack(fmt, string)

struct.unpack_from(fmt, buffer [, offset=0])

struct.calcsize(fmt) (计算fmt的长度)

上面主要是直接使用struct模块的方法,每个fmt都需要单独进行,如果需要重用,可以使用struct提供的Struct类,使用fmt实例化Struct对象之后,调用类似方法就可以进行重用,而且这样使用对象调用的性能更好,比直接使用上述方法调用效率更高。

pack(v1,v2…)

pack_into(buffer, offset, v1, v2 …)

unpack(string)

unpack_from(buffer, offset=0)

format : 返回实例化Struct对象使用的fmt字符串

size:返回fmt字符串的长度

其中最关键的format字符串的使用。

首先是字节顺序:

Character

Byte order

Size

Alignment

@

native

native

native

=

native

standard

none

<

little-endian

standard

none

>

big-endian

standard

none

!

network (= big-endian)

standard

none

然后就是format使用特殊字符,见下表:

Format

C Type

Python type

Standard size

x

pad byte

no value

c

char

string of length 1

1

b

signed char

integer

1

B

unsigned char

integer

1

?

_Bool

bool

1

h

short

integer

2

H

unsigned short

integer

2

i

int

integer

4

I

unsigned int

integer

4

l

long

integer

4

L

unsigned long

integer

4

q

long long

integer

8

Q

unsigned long long

integer

8

f

float

float

4

d

double

float

8

s

char[]

string

p

char[]

string

P

void *

integer

3 加密解密处理

hashlib标准库模块提供了常用的所有加密解密hash方法,使用到的有:

hashlib.update(arg):将hash对象使用arg字符串更新,多次调用相当于将所有arg字符串连接到一起

hashlib.digest() : 返回传如到update方法的字符串的hash值

hashlib.hexdigest():返回hash值的十六进制字符串表示

hashlib.copy():返回一个hash值的副本

websocket中在握手阶段需要获取到客户端的key,然后使用sha1和base64进行加密处理后发送到客户端进行握手。

sha1Encrypt = sha1(key + ‘258EAFA5-E914-47DA-95CA-C5AB0DC85B11‘).digest()

acceptKey = base64.b64encode(sha1Encrypt)

总体来说,使用python实现这些操作非常方便,与php相比更加简洁,彰显了python语言简洁的本质!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值