之前筆記裡面記錄的比較亂,最後我寫了乙個類,試著封裝成乙個模組的樣子。
使用的時候通過繼承生成乙個子類,然後呼叫run執行。
你應該需要重構其中的部分方法,另外可能還需要在子類中建立新的方法。
至少需要重構onrecv方法,接收到資料後的處理。
另外要發資料,呼叫send_data介面,把conn連線和bytes型別的data傳入。
import logging
import queue
import select
import socket
class
server
(object):
def__init__
(self, host, port, data_size=1024):
self.host = host
self.port = port
self.data_size = data_size
self.server = socket.socket()
self.server.setblocking(false)
self.server.setsockopt(socket.sol_socket, socket.so_keepalive, 1)
self.server.setsockopt(socket.sol_socket, socket.so_reuseaddr, 1)
self.server.bind((self.host, self.port))
self.server.listen()
logging.critical("監聽已經開啟")
self.inputs = [self.server, ]
self.outputs =
self.data_queue = {}
defrun(self):
while
true:
self.loop()
defloop
(self):
"""呼叫一次select並處理
run方法中就是迴圈呼叫執行此方法
可以重構此方法:
呼叫執行父類中的這個方法後,加入一次迴圈中需要執行的其他步驟
"""readable, writeable, exceptional = select.select(self.inputs, self.outputs, self.inputs)
logging.debug("select返回: "
% (readable, writeable, exceptional))
for r in readable:
if r is self.server:
conn, addr = r.accept()
logging.info("接收到新的客戶端連線: " % (conn, addr))
conn.setblocking(false)
self.data_queue[conn] = queue.queue()
else:
try:
data = r.recv(self.data_size)
except connectionreseterror as e:
logging.error("recv時捕獲到異常: %s" % e)
self.clean(r)
if r in writeable: writeable.remove(r)
except exception as e:
logging.critical("recv時捕獲未知異常: %s" % e)
self.clean(r)
else:
if data: # 如果有收到資料
logging.debug(data)
self.onrecv(r, data)
else: # 可能是收到了空,就是客戶端斷開了
logging.info("客戶端已斷開: %s" % r)
self.clean(r)
# 只在writeable之前才從writeable列表中清除
if r in writeable: writeable.remove(r)
for w in writeable:
try:
data = self.data_queue[w].get_nowait()
except keyerror as e: # 客戶端突然斷開,這個連線可能會同時出現在讀和寫列表中,而讀中已經將它刪掉了
logging.error("獲取訊息佇列時捕獲到異常: %s" % e)
# self.clean(w) # 這裡就是被刪了才出的異常
except queue.empty: # 如果佇列空了才說明訊息都發完了,從讀列表中remove
# if w in self.outputs: self.outputs.remove(w)
self.outputs.remove(w) # 這裡應該不用判斷,一定在列表裡。因為如果不在會包keyerror
except exception as e:
logging.critical("獲取訊息佇列時捕獲未知異常: %s" % e)
self.clean(w)
else:
if data:
try:
w.sendall(data) # 可能一次發不完,所以send之後不從讀列表中remove。下次會發現隊列為空
except connectionreseterror as e:
logging.error("send時捕獲到異常: %s" % e)
except exception as e:
logging.critical("send時捕獲未知異常: %s" % e)
for e in exceptional:
logging.critical("異常列表有返回: %s" % e)
self.clean(e)
defclean
(self, conn):
"""清理客戶端連線資訊
連線斷開時處理以下4步
1. 從讀列表中去掉,不再去監聽這個連線
2. 從寫列表中去掉,如果還有沒發出的訊息,那麼也不再發了
3. 關閉這個連線
4. 從訊息佇列字典中中刪除這個佇列,可能還有未傳送的訊息
可以重構此方法:
如果還有其他在子類中定義的列表或字典需要清理,
那麼重構此方法,呼叫執行父類的次方法後,新增自定義的內容
"""if conn in self.inputs: self.inputs.remove(conn)
if conn in self.outputs: self.outputs.remove(conn)
conn.close()
if conn in self.data_queue: del self.data_queue[conn]
defonrecv
(self, conn, data):
"""收到訊息後呼叫執行此方法
需要重構此方法
否則是呼叫send_data方法,原樣發回
"""self.send_data(conn, data)
defsend_data
(self, conn, data):
"""傳送資料"""
if conn not
self.data_queue[conn].put(data)
if __name__ == '__main__':
server('localhost', 9999).run()
下面是測試併發的客戶端程式,通過多執行緒實現併發。
import socket
import threading
host = 'localhost'
port = 9999
defclient
(i):
client = socket.socket()
client.connect((host, port))
for j in range(500):
msg = "hello %s %s" % (i, j)
client.send(msg.encode('utf-8'))
data = client.recv(1024)
print('received:', data.decode('utf-8'))
client.close()
if __name__ == '__main__':
for i in range(50):
t = threading.thread(target=client, args=(i,))
t.start()
用select實現點菜功能
bin bash echo a.txt ps3 請問您要吃什麼?echo 滿60元打八 dazhe do let sa n done echo 總計 sa元 if sa gt 60 then sa echo scale 4 sa 0.8 bc fiecho 折後 sa元 fenshu select ...
用select模式實現TCP和UDP的混合監聽
selecttestserver.cpp 定義控制台應用程式的入口點。tcp udp復用server select非阻塞模式 ip 127.0.0.1 tcp port 5001 udp port 5000 include stdafx.h include pragma comment lib,ws...
用verilog實現的串列埠通訊模組
串列埠功能 1 8個資料位 1個停止為 無校驗位 2 空閒時資料線為高電平,從高電平跳向低電平表示啟動訊號 3 波特率可以通過parameter引數實現可調 有兩個檔案 uart.v 串列埠模組 module uart 全域性時鐘復位訊號 iclk,irst n,序列資料線 irx,序列接收 otx...