1. 程式人生 > >2018.11.14

2018.11.14

2018.11.14
1.網路傳輸中的兩個階段 分別是 waitdata 和 copydata
send copydata
recv waitdata copydata

2.阻塞IO
無論是執行緒 程序 還是執行緒 程序池 統統都是阻塞IO

3.非阻塞IO
最直接體現 所有和讀寫相關的函式 都不會阻塞
意味著 在讀寫時 並不能確定目前是否可以讀寫 一旦不能讀寫就丟擲異常
只能使用try except 來判斷是否可以讀寫
必須不斷的執行系統呼叫 CPU佔用特別高 當沒有任何資料要處理的時候簡直就是病毒


伺服器
from concurrent.futures import ThreadPoolExecutor
import socket

server = socket.socket()
# 重用埠
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

server.bind(("192.168.11.210",9999))

server.listen(5)

# 設定是否為阻塞 預設阻塞
server.setblocking(False)

def data_handler(conn):
print("一個新連線..")
while True:
data = conn.recv(1024)
conn.send(data.upper())
# 已連線的客戶端
clients = []
# 需要傳送的資料
send_datas = []
# 已經發送完的 需要刪除的資料
del_datas = []
# 待關閉的客戶端
closed_cs = []
import time
while True:
try:
conn,addr = server.accept()
# 切到處理資料的任務去執行
# 程式碼走到這裡才算是連線成功
# 把連線成功的客戶端存起來
clients.append(conn)
except BlockingIOError:
# print("沒有可以處理的連線 就幹別的活兒")
#要處理的是已經連線成功的客戶端
# 接收資料
for c in clients:
try:
data = c.recv(1024)
if not data:
# 對方關閉了連線
c.close()
# 從客戶端列表中刪除它
closed_cs.append(c)
continue
print("收到%s" % data.decode("utf-8"))
# 現在非阻塞 send直接往快取賽 如果快取滿了 肯定有錯誤 需要單獨處理髮送
# c.send(data.upper())
send_datas.append((c,data))
except BlockingIOError:
pass
except ConnectionResetError:
# 對方關閉了連線
c.close()
# 從客戶端列表中刪除它
closed_cs.append(c)
# 處理髮送資料
for data in send_datas:
try:
data[0].send(data[1].upper())
# 傳送成功需要刪除 不能直接刪除
# send_datas.remove(data)
del_datas.append(data)
except BlockingIOError:
continue
except ConnectionResetError:
# 客戶端連線需要刪除
data[0].close()
closed_cs.append(data[0])
# 等待發送的資料需要刪除
del_datas.append(data)
# 刪除無用的資料
for d in del_datas:
#從待發送的列表中刪除
send_datas.remove(d)
del_datas.clear()
for c in closed_cs:
clients.remove(c)
closed_cs.clear()

客戶端
import socket

c = socket.socket()

c.connect(("127.0.0.1",9999))

while True:
msg = input(">>>:")
if not msg:continue
c.send(msg.encode("utf-8"))
data = c.recv(1024)
print(data.decode("utf-8"))

 

 


4.多路複用
核心函式select
幫你檢測所有的連線 找出可以被處理(可以讀寫)的連線

作為處理資料的一方 不在需要重複去向系統詢問 select給你誰 你就處理誰
沒給就不處理

伺服器
from concurrent.futures import ThreadPoolExecutor
import socket
import select
# select 幫你從一堆連線中找出來需要被處理的連線

server = socket.socket()
# 重用埠
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

server.bind(("192.168.11.210",9999))

server.listen(5)

# 設定是否為阻塞 預設阻塞
server.setblocking(False)

def data_handler(conn):
print("一個新連線..")
while True:
data = conn.recv(1024)
conn.send(data.upper())

# 需要檢測的 是否可讀取的列表 (recv就是一個讀取操作)
rlist = [server,]
# 需要檢測的 是否寫入的列表 (send就是寫入操作)
wlist = []

# 需要傳送的資料 目前是因為 我們要把接收的資料在發回去 所以搞了這個東西 正常沒有這種需求
# 目前客戶端與伺服器端 互動 是必須客戶端傳送資料 伺服器端才能返回資料 正常沒有這種需求
dic = {}


while True: # 用於檢測需要處理的連線 需要不斷檢測 所以迴圈
# rl目前可讀的客戶端列表 wl目前可寫的客戶端列表
rl,wl,xl = select.select(rlist,wlist,[]) # select預設阻塞 阻塞到任意一個連線可以被處理
print(len(rl))
# 處理可讀的socket
for c in rl:
# 無論是客戶端還是伺服器只要可讀就會執行到這裡
if c == server:
# 接收客戶端的連線請求 (一個讀操作)
conn,addr = c.accept()
# 將新連線也交給select來檢測
rlist.append(conn)
else:# 不是伺服器 就是客戶端 客戶端可讀 可以執行recv
try:
data = c.recv(1024)
if not data:
c.close()
rlist.remove(c)
print("%s 傳送 %s" % (c,data.decode("utf-8")))
# 給客戶端傳送資料 前要保證目前可以傳送 將客戶端加入檢測列表
wlist.append(c) # 正常開發中 不可能必須客戶端傳送資料過來後 才能 給客戶端傳送
# 所以這個新增到檢測列表的操作 應該建立連線後立即執行
# 要傳送的資料
dic[c] = data
except ConnectionResetError:
# 客戶端關閉連線
c.close()
rlist.remove(c)
# 處理可寫的socket
for c in wl:
print(c)
try:
c.send(dic[c].upper())
# 刪除資料
dic.pop(c)
# 從檢測列表中刪除已傳送完成的客戶端
wlist.remove(c)
except ConnectionResetError:
c.close() # 關閉連線
dic.pop(c) # 刪除要傳送的資料
wlist.remove(c) # 從待檢測的列表中刪除
except BlockingIOError:#可能快取滿了 發不了
pass

客戶端
import socket

c = socket.socket()

c.connect(("192.168.11.210",9999))

while True:
msg = input(">>>:")
if not msg:continue
c.send(msg.encode("utf-8"))
data = c.recv(1024)
print(data.decode("utf-8"))