Python實現網絡多人聊天室 - Linux
相關連接:
文件結構:
chatroom
├── client.py # 客戶端代碼
├── language.py # 語言文件
├── server.py # 服務端代碼
└── settings.py # 設置文件
0 directories, 4 files
使用模塊:
- os
- sys
- socket
- select
思路:
- settings.py,定義HOST、PORT、ADDR、buffersize、language、curuser等變量。
- server.py,服務器代碼,使用select模塊select方法實現IO多路復用監聽sys.stdin輸入以及客戶端連接,實現與客戶端通信,將從客戶端接收到的信息群發給每個客戶端。
- client.py,客戶端代碼,同樣使用IO多路復用同時監聽客戶端接收信息以及sys.stdin輸入信息,實現與服務端的通信,間接實現與其他客戶端的群聊。
- language.py,語言文件,支持中文以及英語。
代碼:
settings.py
# settings.py HOST = '0.0.0.0' # 主機名 PORT = 5555 # 端口號 buffersize = 1024 # 緩沖大小 ADDR = HOST, PORT # 地址 languages = ['cn', 'en'] # 'cn' -> 中文 language = 'cn' # 'en' -> 英文 curuser = '' # 當前用戶
language.py
# language.py from settings import language if language == 'en': administrator = 'Administrator' txt_administrator_close_chatroom = 'Chatroom closed by Administrator.' txt_uesr_enter_chatroom = 'entered the chatroom.' txt_user_quit_chatroom = 'quited the chatroom.' txt_username = 'username> ' txt_user_already_exists = 'Username already exists!' txt_connect_to = 'Connected to' txt_connect_from = 'Connected from' elif language == 'cn': administrator = '管理員' txt_administrator_close_chatroom = '管理員關閉了聊天室。' txt_uesr_enter_chatroom = '進入了聊天室。' txt_user_quit_chatroom = '退出了聊天室。' txt_username = '用戶名> ' txt_user_already_exists = '用戶名已存在。' txt_connect_to = '連接到' txt_connect_from = '連接從'
server.py
# server.py # 導入系統模塊 import os, sys # 導入網絡編程(傳輸層)模塊 from socket import * # IO多路復用模塊 from select import select # 設置模塊 from settings import * # 語言模塊 from language import * def main(): 'main 主函數' server = socket(AF_INET, SOCK_STREAM) # 建立TCP套接字 server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 設置端口可立即重用 server.bind(ADDR) # 綁定地址 server.listen() # 監聽 # 接收函數 accept(server) def accept(server): 'accept 服務器接受函數' # 使用select模塊的select方法實現IO多路復用監聽傳輸 rlist = [server, sys.stdin] wlist = [] xlist = [] while True: rs, ws, xs = select(rlist, wlist, xlist) for r in rs: if r is server: # 服務器接受客戶端連接 conn, addr = server.accept() # 調用validate函數檢查用戶名 if validate(conn): # 將客戶端套接字添加到rlist中以監聽 rlist.append(conn) # 如果用戶名注冊成功 print(txt_connect_from, addr) else: conn.close() elif r is sys.stdin: # 服務器向所有客戶端發送系統(管理員)消息 data = sys.stdin.readline() if data == '\n': # 如果服務器輸入回車,則退出 for c in rlist[2:]: c.send(b'\n') c.close() server.close() print(txt_administrator_close_chatroom) os._exit(0) else: # 如果服務器輸入正常語句,通知所有客戶端 data = administrator + ': ' + data for c in rlist[2:]: c.send(data.encode()) else: # 服務器接受客戶端的消息並轉發給所有客戶端 data = r.recv(buffersize) if not data: # 關閉客戶端 r.close() rlist.remove(r) else: # 轉發信息給其他客戶端 print(data.decode(), end='') for c in rlist[2:]: if c is not r: c.send(data) def validate(client): '檢驗用戶名 validate username' name = client.recv(buffersize).decode() # print(name.decode()) # print(users) if name in users: client.send(b'Username already exists!') return False else: users.append(name) client.send(b'Welcome!') return True if __name__ == '__main__': # 全局變量,管理用戶信息 users = [] # 主函數 main()
client.py
# client.py # 導入系統模塊 import os, sys # 導入網絡編程(傳輸層)模塊 from socket import * # IO多路復用模塊 from select import select # 設置模塊 from settings import * # 語言模塊 from language import * def main(): 'main 主函數' client = socket(AF_INET, SOCK_STREAM) # 建立TCP套接字 # 登錄函數 if login(client): # 連接函數 connect(client) def connect(client): 'connect 客戶端連接函數' # 使用select模塊的select方法實現IO多路復用監聽傳輸 rlist = [client, sys.stdin] wlist = [] xlist = [] while True: rs, ws, xs = select(rlist, wlist, xlist) for r in rs: if r is client: # 接受服務器發來的消息 data = client.recv(buffersize) if data.decode() == '\n': # 如果消息為回車,聊天室關閉 client.close() print(txt_administrator_close_chatroom) os._exit(0) else: # 打印接收到的信息 print(data.decode(), end='') elif r is sys.stdin: # 發送消息給服務器 data = sys.stdin.readline() if data == '\n': # 如果回車,發送退出消息,關閉客戶端,退出聊天室 data = curuser + ': ' + txt_user_quit_chatroom + '\n' client.send(data.encode()) client.close() os._exit(0) else: # 發信息給服務器 data = curuser + ': ' + data client.send(data.encode()) def login(client): '登錄函數 login' # 使用全局變量管理用戶 # 先讓客戶端輸入姓名 global curuser curuser = input(txt_username) # 再連接到服務器,傳送用戶名以檢驗 client.connect(ADDR) # 連接到服務器地址 print(txt_connect_to, ADDR) client.send(curuser.encode()) data = client.recv(buffersize) if data.decode() == 'Username already exists!': # 如果用戶名已經存在,要求重新輸入 print(txt_user_already_exists) return False else: # 發送信息給服務器,告知服務器用戶進入聊天室 # -*- 因為監聽傳輸的是sys.stdin.readline(),所以必須在最后添加換行符,以便清除阻塞 -*- data = curuser + ': ' + txt_uesr_enter_chatroom + '\n' client.send(data.encode()) return True if __name__ == '__main__': main()
運行截圖:
總結:
- 在打代碼之前,一定要先進行規划,大致寫出項目的大概路線。
- 項目的實現要從最基本的地基開始,像這樣一個網絡間的多人聊天室的實現必須先從建立服務端和客戶端開始,不能反而從表面入手。比如如果要做一個網絡多人聊天室的圖形化界面應用,絕對不可以先去寫圖形界面的實現,就算最后實現的圖形界面多么好看,如果不能實現網絡通信也白干。
- 對於項目中出錯的點,應該多加注釋,方便以后閱讀,在網上查找到有益的知識,可以把網址復制下來,寫進項目文檔,方便以后不時之需。