程序實現: 1、單或多客戶端使用 telnet 登陸服務端 ( 可遠程 ) 進行會話 2、服務端實現登陸、注冊、退出功能 3、客戶端發送的消息會被廣播到已經登陸的其他用戶界面 4、連接到服務端后,可以執行相應的程序指令 程序代碼:https://coding.net/u/wangxiaoqiangs/p/pycode/git/tree/master/socket/GServer GServer.py #!/usr/bin/env python # coding: utf-8 # author: Xiao Guaishou import socket from db import DB from threading import currentThread, Thread class HandlerThread(object): queue = [] # sockect 隊列 db = DB() def __init__(self, sock): self.sock = sock def recv(self): data = self.sock.recv(1024).strip() # 如果使用 while 接收數據時,會導致用戶必須多敲一次回車鍵 return data def send(self, data): self.sock.sendall('\n[System]: %s\n' % data) # 向隊列中廣播消息 def broadcast(self, user, data): for sock in self.queue: sock.sendall('\n[%s]: %s\n' % (user, data)) # 關閉客戶端連接 def stop(self): self.send('ByeBye!') self.sock.close() self.queue.remove(self.sock) # 關閉連接后,記得從隊列中刪除 # 程序入口 def handler(self): funcdict = { 'login': self.login, 'register': self.register } try: thname = currentThread().getName() print('[%s] Got connection from %s' % (thname, self.sock.getpeername())) # 該程序中所有 print 的數據,將全部使用 loging 模塊代替 self.send('請選擇功能:login/register/exit') data = self.recv() if data == 'exit': self.stop() # 其實這里應該單獨使用 self.sock.close() 來關閉連接,因為這時隊列中並沒有該連接,不過有了下面的捕獲就沒有問題了 ^_^ elif data in funcdict: return funcdict.get(data)() else: self.handler() except: # 如果這里不捕獲一下,就無法正常斷開客戶端連接 pass # 處理用戶登陸 def login(self): self.send('Login... 請輸入用戶名密碼,格式:User Password,輸入 Server: 執行程序指令!') user_data = self.recv() # 程序內部指令 if user_data == 'Server:': self.send('\n\tServer:use reged\t切換到注冊頁\n\tServer:exit\t\t退出系統') user_data = self.recv() if user_data == 'Server:use reged': self.register() elif user_data == 'Server:exit': self.stop() else: self.send('輸入錯誤...') datalist = user_data.split() # 判斷用戶輸入,格式是否正確 if len(datalist) == 2: user = datalist[0] password = datalist[1] db_data = self.db.get_data() or {} if user in db_data and password == db_data.get(user): self.queue.append(self.sock) # 有權限登陸系統者,連接被加入到隊列中 self.send('歡迎加入聊天室,輸入 Server: 獲取功能方法!') self.broadcast('System', '[%s] 加入聊天室!' % user) self.chat_room(user) else: self.send('用戶名、密碼錯誤!') self.login() self.login() def register(self): self.send('Register... 請輸入用戶名密碼,格式:User Password,輸入 Server: 執行程序指令!') user_data = self.recv() if user_data == 'Server:': self.send('\n\tServer:use login\t切換到注冊頁\n\tServer:exit\t\t退出系統') user_data = self.recv() if user_data == 'Server:login': self.login() elif user_data == 'Server:exit': self.stop() else: self.send('輸入錯誤...') datalist = user_data.split() if len(datalist) == 2: user = datalist[0] password = datalist[1] db_data = self.db.get_data() or {} if user in db_data: self.send('該用戶名已被注冊!') self.register() else: db_data[user] = password self.db.put_data(db_data) self.queue.append(self.sock) self.broadcast('System', '新用戶 [%s] 加入聊天室!' % user) self.chat_room(user) self.register() def chat_room(self, user): user_data = self.recv() if user_data == 'Server:': self.send('\n\tServer:logout\t退出聊天室') user_data = self.recv() if user_data == 'Server:logout': self.stop() return # 這里如果不加 return ,會將客戶端執行的 Server: 指令也廣播出去 else: self.send('輸入錯誤...') self.chat_room(user) else: self.broadcast(user, user_data) self.chat_room(user) # 為每連接創建線程 def Startthread(sock, addr): print('Received new client connection. %s:%s' % (addr[0], addr[1])) th = HandlerThread(sock) t = Thread(target=th.handler) t.setDaemon(True) t.start() # 啟動服務 def Server(): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0', 17170)) s.listen(1) while True: try: sock, addr = s.accept() except KeyboardInterrupt: exit('\nByeBye!') Startthread(sock, addr) s.close() if __name__ == '__main__': Server() db.py # coding: utf-8 import json # 創建一個類,代替數據庫 class DB(object): def __init__(self, path='Storage.db'): self.path = path def get_data(self, data=None): try: with open(self.path) as f: data = json.load(f) except IOError as e: return data # 首次取數據時,由於文件不存在或沒數據,將返回默認值 None finally: return data def put_data(self, data): with open(self.path, 'w') as f: json.dump(data, f)
