標籤:python websocket 模組學習與總結
之前曾有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語言簡潔的本質!
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
python實現的websocket總結 —— wspy