# 利用python的select模塊實現簡單的Socket Sever
#實現多用戶訪問,再次基礎上可以實現FTP Server應用程序
# 發布目的,在於解決了客戶端強行終止時,服務器端也跟着程序終止
# 程序的關鍵在:讀就是讀,寫就是寫 ,不要讀寫混着來
# 代碼如下:
Server
1 __author__ = 'Stone' 2 # -*- coding: UTF-8 -*- 3 # !/usr/bin/env python3 4 import socket 5 import queue 6 import select 7 HOST = '0.0.0.0' 8 PORT = 8000 9 s = socket.socket() 10 s.bind((HOST,PORT)) 11 s.listen(500) 12 # 設定關閉程序后,能馬上釋放服務器的端口,供后續程序使用 13 s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 14 s.setblocking(0) # 設置為非阻塞模式 15 # 設置列表,新建、發送、異常 16 inputs = [] 17 outputs = [] 18 #exceptions = [] 19 # 存放信息 20 msg_dic = {} 21 inputs.append(s) # 把自身加進去,偵聽是否有新連接 22 while True: 23 readable,writable,exceptional = select.select(inputs,outputs,inputs) # select 會阻塞socket 22 25 for r in readable: 26 if r is s: 27 # 說明有新連接過來 28 conn,addr = s.accept() 29 print("新連接:",addr) 30 conn.setblocking(0) # 把單個連接也設置為非阻塞模式,比如:某個連接接收的文件比較大,將會一直占用着(別的程序沒機會處理);設置為非阻塞后,可以等着下次for循環繼續接收 31 inputs.append(conn) 32 msg_dic[conn] = queue.Queue() # 為每個連接創建消息隊列 33 else: 34 # 說明有連接是活動的 35 try: 36 data = r.recv(1024) 37 if data: 38 print("接收到了數據:",data.decode('utf-8')) 39 # 放進消息隊列 40 msg_dic[r].put(data) 41 if r not in outputs: 42 outputs.append(r) # 並放入發送數據列表 43 else: 44 # 連接斷開 45 print("客戶端斷開連接") 46 if r in outputs: 47 outputs.remove(r) 48 inputs.remove(r) 49 del msg_dic[r] 50 except socket.error: # 解決問題(出現客戶端異常斷開,服務器也跟着斷開),后面不做任何處理,留給exceptional做處理 51 pass 52 53 for w in writable: 54 try: 55 send_msg = msg_dic[w].get_nowait() 56 except queue.Empty: 57 print("client [%s]"% w.getpeername()[0],"queue is empty...") 58 outputs.remove(w) 59 else: 60 print("sending message to [%s]"% w.getpeername()[0],send_msg) 61 w.send(send_msg) 62 outputs.remove(w) # 防止再次執行時,發生empty的異常 63 64 for e in exceptional: 65 if e in outputs: 66 outputs.remove(e) 67 inputs.remove(e) 68 e.close() 69 del msg_dic[e] 70 s.close()
Client:
1 __author__ = 'Stone' 2 # -*- coding: UTF-8 -*- 3 # !/usr/bin/env python3 4 import socket 5 HOST = 'localhost' 6 PORT = 8000 7 s_client = socket.socket() 8 s_client.connect((HOST,PORT)) 9 while True: 10 data = input('>>:').strip() 11 if not data: 12 continue 13 s_client.send(data.encode('utf-8')) 14 recv = s_client.recv(1024) 15 print(recv) 16 #break # 測試,作為客戶端自動離開 17 s_client.close()