項目名稱:多人聊天室
項目結構:
client.py
server.py
settings.py
項目思路:服務端接收客戶端連接,客戶端發送信息給服務端,服務端將信息發送給所有客戶端。
項目實現:主進程負責接收鍵盤輸入(sys.stdin.readline),使用multiprocessing.Process函數創造一個進程,在這個進程中,使用select監聽兩個套接字,一個套接字負責服務端與客戶端之間的消息接收與發送,另一個負責與主進程保持聯系。

# settings.py import os from socket import * from random import randint import shelve HOST = "127.0.0.1" # 服務端和客戶端的連接地址 SOCK_PORT = 4444 SOCK_ADDR = HOST, SOCK_PORT # 服務端server.py文件中供pipe_server和pipe_client使用的套接字地址 SER_PIPE_PORT = 4321 SER_PIPE_ADDR = HOST, SER_PIPE_PORT # 客戶端client.py文件中供pipe_server和pipe_client使用的套接字地址 # 因為每個客戶端都必須有不同的套接字來作起到連接鍵盤輸入和網絡套接字之間的管道的作用 # 使用一個文件記錄下每一次運行出現的端口號,以保證不重復 if not os.path.exists("ports.dat"): f = shelve.open("ports") f["ports"] = [] f = shelve.open("ports") while True: n = randint(4500, 10000) if n not in f["ports"]: f['ports'].append(n) break f.close() CLI_PIPE_PORT = n CLI_PIPE_ADDR = HOST, CLI_PIPE_PORT # 緩沖區大小 BUFFERSIZE = 1024 # 返回一個TCP服務端套接字 def server(addr): sock = socket(AF_INET, SOCK_STREAM, 0) sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind(addr) sock.listen(10) return sock # 返回一個TCP客戶端套接字 def client(addr): sock = socket(AF_INET, SOCK_STREAM, 0) sock.connect(addr) return sock

# server.py import sys import shelve from socket import * from select import select from multiprocessing import Process from settings import * def listen(sock_server, pipe_server): # IO多路復用:循環監聽套接字 rlist = [sock_server, pipe_server] wlist = [] xlist = [] print("等待連接...") while True: rs, ws, xs = select(rlist, wlist, xlist) for r in rs: if r is sock_server: # 接受客戶端連接 conn, addr = sock_server.accept() rlist.append(conn) elif r is pipe_server: # 接收鍵盤輸入並發送到所有客戶端去 conn, addr = pipe_server.accept() data = conn.recv(BUFFERSIZE) data = bytes("管理員:", "UTF-8") + data for c in rlist[2:]: c.send(data) conn.close() else: # 接收客戶端信息 # 將客戶端信息發送到所有的客戶端中去 try: data = r.recv(BUFFERSIZE) except: r.close() rlist.remove(r) else: print(data.decode(), end="") for c in rlist[2:]: c.send(data) def clear_all(): f = shelve.open("ports") f['ports'].clear() f.close() if __name__ == '__main__': # 首先將ports內容都刪除 clear_all() # 創建兩個套接字 # 套接字sock_server是一個TCP服務端,負責服務端與客戶端的交流 # 套接字pipe_server也是一個TCP服務端,不過起到管道的作用,負責接收鍵盤輸入 sock_server = server(SOCK_ADDR) pipe_server = server(SER_PIPE_ADDR) # 開始一個子進程,執行listen函數 p = Process(target=listen, args=(sock_server, pipe_server)) p.daemon = True p.start() # 循環接收鍵盤輸入 while True: try: # 從標准輸入流(鍵盤)讀取一行 data = sys.stdin.readline() except KeyboardInterrupt: # 如果遇到退出/中止信號,關閉套接字,結束子進程,退出程序 sock_server.close() pipe_server.close() p.terminate() clear_all() break if not data: # 如果從鍵盤獲取數據為空,繼續循環 continue else: # 獲得鍵盤數據,創建客戶端套接字pipe_client,將鍵盤輸入傳輸給pipe_server pipe_client = client(SER_PIPE_ADDR) pipe_client.send(bytes(data, "UTF-8")) pipe_client.close()

# client.py import sys from socket import * from select import select from multiprocessing import Process from settings import * def connect(sock_client, pipe_server, name): # IO多路復用:循環監聽套接字 rlist = [sock_client, pipe_server] wlist = [] xlist = [] while True: rs, ws, xs = select(rlist, wlist, xlist) for r in rs: if r is sock_client: # 接受服務端的信息 data = sock_client.recv(BUFFERSIZE).decode() print(data, end="") elif r is pipe_server: # 接受鍵盤輸入並發送給服務端 conn, addr = pipe_server.accept() data = conn.recv(BUFFERSIZE) data = bytes(name + ":", "UTF-8") + data sock_client.send(data) conn.close() def get_name(): return input("User name: ") if __name__ == '__main__': # 使用get_name函數獲得用戶名 name = get_name() # 創建兩個套接字 # 套接字sock_client是一個TCP客戶端,負責服務端與客戶端的交流 # 套接字pipe_server也是一個TCP服務端,不過起到管道的作用,負責接收鍵盤輸入 sock_client = client(SOCK_ADDR) sock_client.send(bytes(name + "加入了聊天室。\n", "UTF-8")) pipe_server = server(CLI_PIPE_ADDR) # 開始一個子進程,執行connect函數 p = Process(target=connect, args=(sock_client, pipe_server, name)) p.daemon = True p.start() # 循環接收鍵盤輸入 while True: try: # 從標准輸入流(鍵盤)讀取一行 data = sys.stdin.readline() except KeyboardInterrupt: # 如果遇到退出/中止信號,發送退出信息,關閉套接字,結束子進程,退出程序 sock_client.send(bytes(name + "退出了聊天室。\n", "UTF-8")) sock_client.close() pipe_server.close() p.terminate() break if not data: # 如果從鍵盤獲取數據為空,繼續循環 continue else: # 獲得鍵盤數據,創建客戶端套接字pipe_client,將鍵盤輸入傳輸給pipe_server pipe_client = client(CLI_PIPE_ADDR) pipe_client.send(bytes(data, "UTF-8")) pipe_client.close()