1. 使用python Websockets庫建立WebSocket客戶端鏈接
使用with語句進行connect連接後的上下文自動管理,當hello協程退出時,自動關閉該WebSocket連接。
不同於基本示例的是,connect函數中需要指定ssl參數做戚的內容
可以看到與基本示例不同的地方在純森陵於:1.不再使用with進行自動的上下文管理;2.使用while語句進行長連接的處理,如果需要能夠自己控制長連接的關閉,那麼可以設置一個變數比如conn_flag,初始化其值為True,當收到退出連接的命令時,將conn_flag置為False,即可實現連接春運的可控退出。
Enjoy it!
2. 如何使用WebSocket做介面測試
如果遇見了一個全新的協議,怎麼從零開始,完成介面測試?以 WebSocket 為例。
WebSocket 協議在2008年誕生,2011年成為國際標准。現在所有瀏覽器都已經支持了。WebSocket 的最大特點就是,伺服器可以主動向客戶端推送信息,客戶端也可以主動向伺服器發送信息,是真正的雙向平等對話。
WebSocket 的其他特點:
1. 建立在 TCP 協議之上,伺服器端的實現比較容易。
2. 與 HTTP 協議有著良好的兼容性。默認埠也是80和443,並且握手階段採用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理伺服器。
3. 數據格式比較輕量,性能開銷小,通信高效。
4. 可以發送文本,也可以發送二進制數據。
5. 沒有同源限制,客戶端可以與任意伺服器通信。
6. 協議標識符是ws(如果加密,則為wss),伺服器網址就是 URL。
· ws–>http(未加密) 無證書
· wss–>https(加密) 有證書
第一步:
很多時候第一反應向開發工程師求助,因為開發工程師基於新協議已經完成了介面開發,向開發工程師求助顯然是最好的辦法。找到一些學習脈絡,包含了協議的說明文檔、代碼開發文檔、實現代碼等內容,了解協議的原理。向開發求助是個方法。
那麼 WebSocket 用 Fiddler 怎麼搞定?,其實主要就是修改了 Fiddler 中 Rules 下的 Customize Rules,如果感興趣可以自己去搜一下。當面對陌生技術問題的時候,應該使用最熟悉的技術去嘗試解決問題。雖然 Fiddler 截獲 WebSocket 介面的辦法,所截獲的全部消息都在日誌裡面,根本無法操作。但是,可以藉助 Fiddler 分析 WebSocket 的介面,一開始給 Fiddler 這款工具的定位一樣,那就是通過它輔助分析我們的被測介面。處理HTTP、HTTPS,推薦用Fiddler。
但是在處理TCP,UDP 就用WireShark。Websocket是應用層協議,建立在 TCP 協議之上,伺服器端的實現比較容易。因為應用層是在傳輸層的基礎上包裝數據,所以我們還是從底層開始了解Websocket到底是個啥?是如何工作的?
可以通過---- wireshark(網路封包分析軟體)抓包工具抓到WebSocket介面
wireshark下載地址:https://www.wireshark.org/download.html
以下是python實現的websocket 介面連接。
3. 如何用python獲取websocket數據
這里,介紹如何使用 Python 與前端 js 進行通信。
websocket 使用 HTTP 協議完成握手之後,不通過 HTTP 直接進行 websocket 通信。
於是,使用 websocket 大致兩個步驟:使用 HTTP 握手,通信。
js 處理 websocket 要使用 ws 模塊; python 處理則使用 socket 模塊建立 TCP 連接即可,比一般的 socket ,只多一個握手以及數據處理的步驟。
握手
過程
包格式
js 客戶端先向伺服器端 python 發送握手包,格式如下:
?
1
2
3
4
5
6
7
8
GET
/chat HTTP/1.1
Host:
server.example.com
Upgrade:
websocket
Connection:
Upgrade
Sec-WebSocket-Key:
dGhlIHNhbXBsZSBub25jZQ==
Origin:
Sec-WebSocket-Protocol:
chat, superchat
Sec-WebSocket-Version:
13
伺服器回應包格式:
?
1
2
3
4
5
HTTP/1.1
101 Switching Protocols
Upgrade:
websocket
Connection:
Upgrade
Sec-WebSocket-Accept:
s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol:
chat
其中, Sec-WebSocket-Key 是隨機的,伺服器用這些數據構造一個 SHA-1 信息摘要。
方法為: key+migic , SHA-1 加密, base-64 加密,如下:
Python 中的處理代碼
?
1
2
MAGIC_STRING='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
res_key=base64.b64encode(hashlib.sha1(sec_key
+MAGIC_STRING).digest())
握手完整代碼
js 端
js 中有處理 websocket 的類,初始化後自動發送握手包,如下:
var socket = new WebSocket('ws://localhost:3368');
Python 端
Python 用 socket 接受得到握手字元串,處理後發送
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
HOST='localhost'
PORT=3368
MAGIC_STRING='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HANDSHAKE_STRING="HTTP/1.1
101 Switching Protocols\r\n"
\
"Upgrade:websocket\r\n"\
"Connection:
Upgrade\r\n"
\
"Sec-WebSocket-Accept:
{1}\r\n"
\
"WebSocket-Location:ws://{2}/chat\r\n"
\
"WebSocket-Protocol:chat\r\n\r\n"
defhandshake(con):
#con為用socket,accept()得到的socket
headers={}
shake=con.recv(1024)
ifnot
len(shake):
returnFalse
header,
data =shake.split('\r\n\r\n',1)
forline
inheader.split('\r\n')[1:]:
key,
val =line.split(':
',1)
headers[key]=val
if'Sec-WebSocket-Key'
not
in
headers:
print('This
socket is not websocket, client close.')
con.close()
returnFalse
sec_key=headers['Sec-WebSocket-Key']
res_key=base64.b64encode(hashlib.sha1(sec_key
+MAGIC_STRING).digest())
str_handshake=HANDSHAKE_STRING.replace('{1}',
res_key).replace('{2}',
HOST +':'
+
str(PORT))
printstr_handshake
con.send(str_handshake)
returnTrue
通信
不同版本的瀏覽器定義的數據幀格式不同, Python 發送和接收時都要處理得到符合格式的數據包,才能通信。
Python 接收
Python 接收到瀏覽器發來的數據,要解析後才能得到其中的有用數據。
瀏覽器包格式
固定位元組:
( 1000 0001 或是 1000 0002 )這里沒用,忽略
包長度位元組:
第一位肯定是 1 ,忽略。剩下 7 個位可以得到一個整數 (0 ~ 127) ,其中
( 1-125 )表此位元組為長度位元組,大小即為長度;
(126)表接下來的兩個位元組才是長度;
(127)表接下來的八個位元組才是長度;
用這種變長的方式表示數據長度,節省數據位。
mark 掩碼:
mark 掩碼為包長之後的 4 個位元組,之後的兄弟數據要與 mark 掩碼做運算才能得到真實的數據。
兄弟數據:
得到真實數據的方法:將兄弟數據的每一位 x ,和掩碼的第 i%4 位做 xor 運算,其中 i 是 x 在兄弟數據中的索引。
完整代碼
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
defrecv_data(self,
num):
try:
all_data=self.con.recv(num)
ifnot
len(all_data):
returnFalse
except:
returnFalse
else:
code_len=ord(all_data[1])
& 127
ifcode_len
==126:
masks=all_data[4:8]
data=all_data[8:]
elifcode_len
==127:
masks=all_data[10:14]
data=all_data[14:]
else:
masks=all_data[2:6]
data=all_data[6:]
raw_str=""
i=0
ford
indata:
raw_str+=chr(ord(d)
^ ord(masks[i%4]))
i+=1
returnraw_str
js 端的 ws 對象,通過 ws.send(str) 即可發送
ws.send(str)
Python 發送
Python 要包數據發送,也需要處理,發送包格式如下
固定位元組:固定的 1000 0001( 『 \x81 ′ )
包長:根據發送數據長度是否超過 125 , 0xFFFF(65535) 來生成 1 個或 3 個或 9 個位元組,來代表數據長度。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
defsend_data(self,
data):
ifdata:
data=str(data)
else:
returnFalse
token="\x81"
length=len(data)
iflength
< 126:
token+=struct.pack("B",
length)
eliflength
<=0xFFFF:
token+=struct.pack("!BH",126,
length)
else:
token+=struct.pack("!BQ",127,
length)
#struct為Python中處理二進制數的模塊,二進制流為C,或網路流的形式。
data='%s%s'
%
(token, data)
self.con.send(data)
returnTrue
js 端通過回調函數 ws.onmessage() 接受數據
?
1
2
3
4
5
ws.onmessage
= function(result,nTime){
alert("從服務端收到的數據:");
alert("最近一次發送數據到現在接收一共使用時間:"+
nTime);
console.log(result);
}
最終代碼
Python服務端
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#
_*_ coding:utf-8 _*_
__author__='Patrick'
importsocket
importthreading
importsys
importos
importMySQLdb
importbase64
importhashlib
importstruct
#
====== config ======
HOST='localhost'
PORT=3368
MAGIC_STRING='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
HANDSHAKE_STRING="HTTP/1.1
101 Switching Protocols\r\n"
\
"Upgrade:websocket\r\n"\
"Connection:
Upgrade\r\n"
\
"Sec-WebSocket-Accept:
{1}\r\n"
\
"WebSocket-Location:ws://{2}/chat\r\n"
\
"WebSocket-Protocol:chat\r\n\r\n"
classTh(threading.Thread):
def__init__(self,
connection,):
threading.Thread.__init__(self)
self.con=connection
defrun(self):
whileTrue:
try:
pass
self.con.close()
defrecv_data(self,
num):
try:
all_data=self.con.recv(num)
ifnot
len(all_data):
returnFalse
except:
returnFalse
else:
code_len=ord(all_data[1])
& 127
ifcode_len
==126:
masks=all_data[4:8]
data=all_data[8:]
elifcode_len
==127:
masks=all_data[10:14]
data=all_data[14:]
else:
masks=all_data[2:6]
data=all_data[6:]
raw_str=""
i=0
ford
indata:
raw_str+=chr(ord(d)
^ ord(masks[i%4]))
i+=1
returnraw_str
#
send data
defsend_data(self,
data):
ifdata:
data=str(data)
else:
returnFalse
token="\x81"
length=len(data)
iflength
< 126:
token+=struct.pack("B",
length)
eliflength
<=0xFFFF:
token+=struct.pack("!BH",126,
length)
else:
token+=struct.pack("!BQ",127,
length)
#struct為Python中處理二進制數的模塊,二進制流為C,或網路流的形式。
data='%s%s'
%
(token, data)
self.con.send(data)
returnTrue
#
handshake
defhandshake(con):
headers={}
shake=con.recv(1024)
ifnot
len(shake):
returnFalse
header,
data =shake.split('\r\n\r\n',1)
forline
inheader.split('\r\n')[1:]:
key,
val =line.split(':
',1)
headers[key]=val
if'Sec-WebSocket-Key'
not
in
headers:
print('This
socket is not websocket, client close.')
con.close()
returnFalse
sec_key=headers['Sec-WebSocket-Key']
res_key=base64.b64encode(hashlib.sha1(sec_key
+MAGIC_STRING).digest())
str_handshake=HANDSHAKE_STRING.replace('{1}',
res_key).replace('{2}',
HOST +':'
+
str(PORT))
printstr_handshake
con.send(str_handshake)
returnTrue
defnew_service():
"""start
a service socket and listen
when
coms a connection, start a new thread to handle it"""
sock=socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
try:
sock.bind(('localhost',3368))
sock.listen(1000)
#鏈接隊列大小
print"bind
3368,ready to use"
except:
print("Server
is already running,quit")
sys.exit()
whileTrue:
connection,
address =sock.accept()
#返回元組(socket,add),accept調用時會進入waite狀態
print"Got
connection from ",
address
ifhandshake(connection):
print"handshake
success"
try:
t=Th(connection,
layout)
t.start()
print'new
thread for client ...'
except:
print'start
new thread error'
connection.close()
if__name__
=='__main__':
new_service()
js客戶 端
?
1
2
3
4
5
6
7
8
<script>
varsocket
= newWebSocket('ws://localhost:3368');
ws.onmessage
= function(result,nTime){
alert("從服務端收到的數據:");
alert("最近一次發送數據到現在接收一共使用時間:"+
nTime);
console.log(result);
}
</script>
4. python怎麼連接websocket
websocket是html5引入的一個新特性,傳統的web應用是通過http協議來提供支持,如果要實時同步傳輸數據,需要輪詢,效率低下
websocket是類似socket通信,web端連接伺服器後,握手成功,一直保持連接,可以理解為長連接,這時伺服器就可以主動給客戶端發送數據,實現數據的自動更新。
使用websocket需要注意瀏覽器和當前的版本,不同的瀏覽器提供的支持不一樣,因此設計伺服器的時候,需要考慮。