所有都在代碼中:python
import select import socket import queue """ 簡單的select 實現echo server 我的理解: select 編程思想,讓select去去判斷能不能讀或能不能寫。 若是能讀或能寫,select就會告知readable或writeable,這時就得讓文件句柄去讀或寫, 在這裏是scoket,recv()或scoket.send()去執行實際的操做 """ server = socket.socket() # 獲取一個套接字 server.setblocking(False) # 設置爲非阻塞模式 server.bind(('127.0.0.1',9001)) # 綁定ip和端口 server.listen(5) # 啓動監聽,到這步能夠在netstat 看到監聽狀態,若是沒有server.accept()的話,客戶端的鏈接會被拒絕 inputs = [ server ] # 設置輸入隊列,當server的監聽收到客戶端鏈接時,select會認爲這個server爲可讀,就得讓server.accpet()去接收客戶端鏈接 # 或者當socket有新的數據發送過來時,select會認爲這個socket是可讀的,就得去讓socket的recv()去接收數據 outputs = [] # 當socket能夠發送數據給對端時,select會認定這個socket是 message_queue = {} # 消息隊列,key值爲socket套接字 n = 0 # 體現while循環了多少次 while inputs: # inputs中是否存在值 print("waiting for connect..., %s"%n) readable , writeable ,exceptions = select.select(inputs,outputs,inputs) # select就像一個很是強大的監視器,監視各個文件句柄的是否可讀可寫。 for s in readable: # 循環可讀的。 if s is server: # 若是是server套接字 print("server can read...") conn, addr = s.accept() # 就接收客戶端鏈接 print("welcome (%s,%s) to connect..."%addr) inputs.append(conn) # 將客戶端套鏈接接字放入inputs,而不是立刻接收或發生數據。 # s.setblocking(False) else: # 若是不是server套接字 print("a scoket can read:" ,s.getpeername()) # 只有當有數據發送過來時纔會把那個套接字放在readable裏 try: data = s.recv(1024) # 就代表客戶端鏈接套接字收到了客戶端發送來的數據 except Exception as e: # 客戶端進程被強制關閉,致使客戶端發送rst包。 ConnectionResetError print(e) inputs.remove(s) # 若是客戶端退出,就講這個套接字從inputs中移除 if s in outputs: # 若是這個套接字存在於outputs中,也將這個套接字從outputs中移除,由於客戶端都關閉了 # 還將這個套接字放在outputs中沒有了,發不了數據了啊。 outputs.remove(s) s.close() # 記得關閉這個套接字 break # 這裏要break,由於s.recv(1024)異常了,data根本就沒有被定義,下面的if data就會報錯。 if data: # 若是有數據 print("recv from %s:%s"%(s.getpeername(),data.decode())) message_queue[s] = queue.Queue() # 將收到的數據放入消息隊列中 message_queue[s].put(data) outputs.append(s) # 由於是echo server,全部有道數據後就得原有返回數據,可是這裏不當即返回,而是交由select. else: print("the client (%s,%s) is closed "%s.getpeername()) # 若是data爲空,表示客戶端正常關閉鏈接,客戶端發送了fin. if s in outputs: # 客戶端關閉了,這個套接字也就不必放在outputs和inputs中了 outputs.remove(s) inputs.remove(s) del message_queue[s] # 消息隊列也能夠清除 s.close() # 關閉套接字 for s in writeable: # 若是可寫,「好像ouputs有鏈接的套接字 存在就是可寫的。」 try: data = message_queue[s].get_nowait() # 獲取要發送的消息 except queue.Empty: # 消息隊列爲空也是可寫的。 print("outpt queue for (%s,%s) in empty"%s.getpeername()) outputs.remove(s) # 由於是echo server,因此消息隊列爲空時,說明已經迴應了數據給對端,就不用一直去判斷這個套接字是否可寫了。 # s.close() # 沒有數據可發送並不表明要關閉socket else: print("send data to client (%s,%s)"%s.getpeername()) s.send(data) # outputs.remove(s) # 發送完一條數據後,其實不能將他移除,由於有可能對端很是迅速的發了多條消息過來,而服務器一條消息都還沒來得及回覆, # 服務器的消息將會都放在隊列中等待發送,因此這裏的讓隊列爲空時在把套接字從outputs中移除。 for s in exceptions: # 若是發生錯誤 print("expect happend on (%s,%s)"%s.getpeername()) inputs.remove(s) # 將這個套接字從inouts,outputs中移除 if s in outputs: outputs.remove(s) s.close() # 鏈接要關閉 del message_queue[s] # 消息隊列也要關閉 n += 1 # 看看while循環可多少次
waiting for connect..., 0 server can read... welcome (127.0.0.1,3137) to connect... waiting for connect..., 1 server can read... welcome (127.0.0.1,3138) to connect... waiting for connect..., 2 a scoket can read: ('127.0.0.1', 3137) # 這裏說明第一個客戶端套接字在readable裏,下面也沒看到第二個客戶端在readable裏直到第二個客戶端發送數據過來,也就是說select只是把可讀的返回給了readable裏。 recv from ('127.0.0.1', 3137):this is the 1st client waiting for connect..., 3 send data to client (127.0.0.1,3137) waiting for connect..., 4 outpt queue for (127.0.0.1,3137) in empty waiting for connect..., 5 a scoket can read: ('127.0.0.1', 3138) recv from ('127.0.0.1', 3138):this is the 2nd client waiting for connect..., 6 send data to client (127.0.0.1,3138) waiting for connect..., 7 outpt queue for (127.0.0.1,3138) in empty waiting for connect..., 8 a scoket can read: ('127.0.0.1', 3138) recv from ('127.0.0.1', 3138):2nd haha waiting for connect..., 9 send data to client (127.0.0.1,3138) waiting for connect..., 10 outpt queue for (127.0.0.1,3138) in empty waiting for connect..., 11 a scoket can read: ('127.0.0.1', 3137) recv from ('127.0.0.1', 3137):1st hello waiting for connect..., 12 send data to client (127.0.0.1,3137) waiting for connect..., 13 outpt queue for (127.0.0.1,3137) in empty waiting for connect..., 14 a scoket can read: ('127.0.0.1', 3138) recv from ('127.0.0.1', 3138):2nd wiil killed by os waiting for connect..., 15 send data to client (127.0.0.1,3138) waiting for connect..., 16 outpt queue for (127.0.0.1,3138) in empty waiting for connect..., 17 a scoket can read: ('127.0.0.1', 3138) [WinError 10054] 遠程主機強迫關閉了一個現有的鏈接。 waiting for connect..., 18 a scoket can read: ('127.0.0.1', 3137) recv from ('127.0.0.1', 3137):1st wiil closed by socket.close() waiting for connect..., 19 send data to client (127.0.0.1,3137) waiting for connect..., 20 outpt queue for (127.0.0.1,3137) in empty waiting for connect..., 21 a scoket can read: ('127.0.0.1', 3137) the client (127.0.0.1,3137) is closed waiting for connect..., 22
客戶端:編程
import socket s = socket.socket() s.connect(('127.0.0.1',9001)) while True: data = input(">> ").strip() if data == "": continue if data == "q": s.close() break s.send(bytes(data,encoding="utf-8")) data = s.recv(1024) print("recv:",data.decode())