基于python實現(xiàn)聊天室程序
本文實例為大家分享了python實現(xiàn)簡單聊天室的具體代碼,供大家參考,具體內(nèi)容如下
剛剛接觸python編程,又從接觸java開始一直對socket模塊感興趣,所以就做了一個聊天室的小程序。
該程序由客戶端與服務(wù)器構(gòu)成,使用UDP服務(wù),服務(wù)器端綁定本地IP和端口,客戶端由系統(tǒng)隨機選擇端口。
實現(xiàn)了群發(fā)、私發(fā)、點對點文件互傳功能。
客戶端自建了一個類繼承了Cmd模塊,使用自定義的命令command進行操作,調(diào)用相應(yīng)的do_command方法。
使用json模塊進行消息的封裝序列化,在接收方進行解析。
客戶端代碼如下:
import socket import threading import json import os from cmd import Cmd class Client(Cmd): """ 客戶端 """ prompt = '>>>' intro = '[Welcome] 簡易聊天室客戶端(Cli版)\n' + '[Welcome] 輸入help來獲取幫助\n' buffersize = 1024 def __init__(self, host): """ 構(gòu)造 """ super().__init__() self.__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # self.__id = None self.__nickname = None self.__host = host self.thread_recv = None self.threadisalive = False # 是否在接收文件 self.recvfile = False # 是否在發(fā)送文件 self.sendfile = False self.filesize = None self.sendfilesize = None # 接收文件包計數(shù) self.filecount = None # 接收文件名 self.filename = None # 發(fā)送文件名 self.sendfilename = None # 發(fā)送者 self.filefrom = None # 接收者 self.fileto = None # 接收文件流 self.file_recv = None # 發(fā)送文件流 self.file_send = None # 接收文件地址 self.filefrom_addr = None # 發(fā)送文件地址 self.fileto_addr = None def __receive_message_thread(self): """ 接受消息線程 """ while self.threadisalive: # noinspection PyBroadException try: buffer, addr = self.__socket.recvfrom(1024) ''' 文件流由發(fā)送端直接發(fā)送,不經(jīng)過服務(wù)器,故當發(fā)送端發(fā)來的消息時,將收到的數(shù)據(jù)存入文件 ''' if (addr != self.__host) & (addr == self.filefrom_addr) & self.recvfile: self.file_recv.write(buffer) self.filecount += 1 if self.filecount * 1024 >= self.filesize: self.file_recv.close() print(self.filename, 'is received.') self.recvfile = False continue js = json.loads(buffer.decode()) # 若接收的數(shù)據(jù)為消息信息,則顯示 if js['type'] == 'message': print(js['message']) # 若接收的數(shù)據(jù)為文件發(fā)送請求,則存儲文件信息,并顯示 elif js['type'] == 'filequest': if self.recvfile: self.__socket.sendto(json.dumps({ 'type': 'fileres', 'fileres': 'no', 'nickname': self.__nickname, 'who': js['nickname'], 'errormessage': 'is transfroming files.', }).encode(), self.__host) continue filename = js['filename'] who = js['nickname'] filesize = js['filesize'] self.recvfile = True self.filesize = filesize self.filename = filename self.filecount = 0 self.filefrom = who self.filefrom_addr = (js['send_ip'], js['send_port']) print('[system]:', who, ' send a file(', filename, ') to you. receive? ') # 接受的數(shù)據(jù)為請求回復(fù),若同意接收則存儲服務(wù)器發(fā)來的接收方的地址,并開啟發(fā)送線程 elif js['type'] == 'fileres': if js['fileres'] == 'yes': print(js['recv_ip'], js['recv_port']) self.fileto_addr = (js['recv_ip'], js['recv_port']) thread = threading.Thread( target=self.__send_file_thread) thread.start() else: print(js['nickname'], js['errormessage']) self.sendfile = False except Exception as e: print(e) print('[Client] 無法從服務(wù)器獲取數(shù)據(jù)') def __send_broadcast_message_thread(self, message): """ 發(fā)送廣播消息線程 :param message: 消息內(nèi)容 """ self.__socket.sendto(json.dumps({ 'type': 'broadcast', 'nickname': self.__nickname, 'message': message, }).encode(), self.__host) def __send_file_thread(self): """ 發(fā)送文件線程 :param message: 消息內(nèi)容 """ filecount = 0 print('[system]', 'sending the file...') while filecount * 1024 <= self.sendfilesize: self.__socket.sendto( self.file_send.read(1024), self.fileto_addr) filecount += 1 self.file_send.close() self.sendfile = False print('[system]', 'the file is sended.') def __send_whisper_message_thread(self, who, message): """ 發(fā)送私發(fā)消息線程 :param message: 消息內(nèi)容 """ self.__socket.sendto(json.dumps({ 'type': 'sendto', 'who': who, 'nickname': self.__nickname, 'message': message }).encode(), self.__host) def send_exit(self): self.__socket.sendto(json.dumps({ 'type': 'offline', 'nickname': self.__nickname, }).encode(), self.__host) def start(self): """ 啟動客戶端 """ self.cmdloop() def do_login(self, args): """ 登錄聊天室 :param args: 參數(shù) """ nickname = args.split(' ')[0] # 將昵稱發(fā)送給服務(wù)器,獲取用戶id self.__socket.sendto(json.dumps({ 'type': 'login', 'nickname': nickname, }).encode(), self.__host) # 嘗試接受數(shù)據(jù) buffer = self.__socket.recvfrom(1300)[0].decode() obj = json.loads(buffer) if obj['login'] == 'success': self.__nickname = nickname print('[Client] 成功登錄到聊天室') self.threadisalive = True # 開啟子線程用于接受數(shù)據(jù) self.thread_recv = threading.Thread( target=self.__receive_message_thread) self.thread_recv.setDaemon(True) self.thread_recv.start() else: print('[Client] 無法登錄到聊天室', obj['errormessage']) def do_send(self, args): """ 發(fā)送消息 :param args: 參數(shù) """ if self.__nickname is None: print('請先登錄!login nickname') return message = args # 開啟子線程用于發(fā)送數(shù)據(jù) thread = threading.Thread( target=self.__send_broadcast_message_thread, args=(message, )) thread.setDaemon(True) thread.start() def do_sendto(self, args): """ 發(fā)送私發(fā)消息 :param args: 參數(shù) """ if self.__nickname is None: print('請先登錄!login nickname') return who = args.split(' ')[0] message = args.split(' ')[1] # # 顯示自己發(fā)送的消息 # print('[' + str(self.__nickname) + '(' + str(self.__id) + ')' + ']', message) # 開啟子線程用于發(fā)送數(shù)據(jù) thread = threading.Thread( target=self.__send_whisper_message_thread, args=(who, message)) thread.setDaemon(True) thread.start() def do_catusers(self, arg): if self.__nickname is None: print('請先登錄!login nickname') return catmessage = json.dumps({'type': 'catusers'}) self.__socket.sendto(catmessage.encode(), self.__host) def do_catip(self, args): if self.__nickname is None: print('請先登錄!login nickname') return who = args catipmessage = json.dumps({'type': 'catip', 'who': who}) self.__socket.sendto(catipmessage.encode(), self.__host) def do_help(self, arg): """ 幫助 :param arg: 參數(shù) """ command = arg.split(' ')[0] if command == '': print('[Help] login nickname - 登錄到聊天室,nickname是你選擇的昵稱') print('[Help] send message - 發(fā)送消息,message是你輸入的消息') print('[Help] sendto who message - 私發(fā)消息,who是用戶名,message是你輸入的消息') print('[Help] catusers - 查看所有用戶') print('[Help] catip who - 查看用戶IP,who為用戶名') print('[Help] sendfile who filedir - 向某用戶發(fā)送文件,who為用戶名,filedir為文件路徑') print('[Help] getfile filename who yes/no - 接收文件,filename 為文件名,who為發(fā)送者,yes/no為是否接收') elif command == 'login': print('[Help] login nickname - 登錄到聊天室,nickname是你選擇的昵稱') elif command == 'send': print('[Help] send message - 發(fā)送消息,message是你輸入的消息') elif command == 'sendto': print('[Help] sendto who message - 發(fā)送私發(fā)消息,message是你輸入的消息') else: print('[Help] 沒有查詢到你想要了解的指令') def do_exit(self, arg): # 以do_*開頭為命令 print("Exit") self.send_exit() try: self.threadisalive = False self.thread_recv.join() except Exception as e: print(e) # self.__socket.close() def do_sendfile(self, args): who = args.split(' ')[0] filepath = args.split(' ')[1] filename = filepath.split('\\')[-1] # 判斷是否在發(fā)送文件 if self.sendfile: print('you are sending files, please try later.') return if not os.path.exists(filepath): print('the file is not exist.') return filesize = os.path.getsize(filepath) # print(who, filename, filesize) self.sendfile = True self.fileto = who self.sendfilename = filename self.sendfilesize = filesize self.file_send = open(filepath, 'rb') self.__socket.sendto(json.dumps({ 'type': 'filequest', 'nickname': self.__nickname, 'filename': self.sendfilename, 'filesize': self.sendfilesize, 'who': self.fileto, 'send_ip': '', 'send_port': '', }).encode(), self.__host) print('request send...') # fileres = self.__socket.recvfrom(1024)[0].decode() # js = json.loads(fileres) def do_getfile(self, args): filename = args.split(' ')[0] who = args.split(' ')[1] ch = args.split(' ')[2] # print(self.filename is not None, filename, self.filename, who, self.filefrom) if (self.filename is not None) & (filename == self.filename) & (who == self.filefrom): if ch == 'yes': self.file_recv = open(self.filename, 'wb') self.__socket.sendto(json.dumps({ 'type': 'fileres', 'fileres': 'yes', 'nickname': self.__nickname, 'who': who, 'recv_ip': '', 'recv_port': '', }).encode(), self.__host) print('you agree to reveive the file(', filename, ') from', who) else: self.__socket.sendto(json.dumps({ 'type': 'fileres', 'fileres': 'no', 'nickname': self.__nickname, 'errormessage': 'deny the file.', 'who': who, 'recv_ip': '', 'recv_port': '', }).encode(), self.__host) print('you deny to reveive the file(', filename, ') from', who) self.recvfile = False else: print('the name or sender of the file is wrong.') c = Client(('127.0.0.1', 12346)) c.start()
服務(wù)器端主要進行消息的分類轉(zhuǎn)發(fā)處理,用戶列表、地址列表的維護。
服務(wù)器端代碼如下:
import socket import json obj = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) obj.bind(('127.0.0.1', 12346)) conn_list = [] user_list = [] while True: try: receive_data, client_address = obj.recvfrom(1024) js = json.loads(receive_data.decode()) # 登錄消息 if js['type'] == 'login': nickname = str(js['nickname']) if nickname in user_list: obj.sendto(json.dumps({'login': 'fail', 'errormessage': 'the nickname is exists'}).encode(), client_address) else: # 向其他用戶發(fā)送通知 for i in range(len(conn_list)): obj.sendto(json.dumps( { 'type': 'message', 'message': '[system]' + nickname + '已登錄.' }).encode(), conn_list[i]) user_list.append(nickname) conn_list.append(client_address) print(nickname, client_address, '登錄成功!') obj.sendto(json.dumps({'login': 'success', 'nickname': nickname}).encode(), client_address) # 群發(fā)消息 elif js['type'] == 'broadcast': message = js['message'] nickname = js['nickname'] for i in range(len(conn_list)): obj.sendto(json.dumps( { 'type': 'message', 'message': nickname + ':' + message }).encode(), conn_list[i]) # 私發(fā)消息 elif js['type'] == 'sendto': who = js['who'] nickname = js['nickname'] message = js['message'] # 檢查用戶是否存在 if who not in user_list: obj.sendto(json.dumps( { 'type': 'message', 'message': who + ' not exist or not online.please try later.' }).encode(), client_address) else: obj.sendto(json.dumps( { 'type': 'message', 'message': nickname + ' whisper to you: ' + message }).encode(), conn_list[user_list.index(who)]) # 查看用戶列表 elif js['type'] == 'catusers': users = json.dumps(user_list) obj.sendto(json.dumps( { 'type': 'message', 'message': users, }).encode(), client_address) # 查看用戶IP elif js['type'] == 'catip': who = js['who'] if who not in user_list: obj.sendto(json.dumps( { 'type': 'message', 'message': who + ' not exist or not online.please try later.' }).encode(), client_address) else: addr = json.dumps(conn_list[user_list.index(who)]) obj.sendto(json.dumps( { 'type': 'message', 'message': addr, }).encode(), client_address) # 離線消息 elif js['type'] == 'offline': user_list.remove(js['nickname']) conn_list.remove(client_address) obj.sendto( (js['nickname'] + 'offline.').encode(), client_address) # 向其他用戶發(fā)送通知 for i in range(len(conn_list)): obj.sendto(json.dumps( { 'type': 'message', 'message': '[system]' + nickname + '下線了.' }).encode(), conn_list[i]) # 發(fā)送文件請求 elif js['type'] == 'filequest': who = js['who'] if who not in user_list: obj.sendto(json.dumps( { 'type': 'message', 'message': who + ' not exist or not online.please try later.' }).encode(), client_address) else: js['send_ip'] = client_address[0] js['send_port'] = client_address[1] obj.sendto(json.dumps(js).encode(), conn_list[user_list.index(who)]) print(js['nickname'], 'request to send file to', who) # 發(fā)送文件請求回復(fù) elif js['type'] == 'fileres': who = js['who'] if js['fileres'] == 'yes': js['recv_ip'] = client_address[0] js['recv_port'] = client_address[1] print(js['nickname'], 'agree to receive file from', js['who']) else: print(js['nickname'], 'deny to receive file from', js['who']) obj.sendto(json.dumps(js).encode(), conn_list[user_list.index(who)]) except Exception as e: print(e)
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
機器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨熱One-Hot編碼及其代碼詳解
獨熱編碼即 One-Hot 編碼,又稱一位有效編碼。其方法是使用 N位 狀態(tài)寄存器來對 N個狀態(tài) 進行編碼,每個狀態(tài)都有它獨立的寄存器位,并且在任意時候,其中只有一位有效,這篇文章主要介紹了機器學(xué)習(xí)數(shù)據(jù)預(yù)處理之獨熱One-Hot編碼及其代碼詳解,需要的朋友可以參考下2022-07-07基于python進行桶排序與基數(shù)排序的總結(jié)
今天小編就為大家分享一篇基于python進行桶排序與基數(shù)排序的總結(jié),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05Python實現(xiàn)的序列化和反序列化二叉樹算法示例
這篇文章主要介紹了Python實現(xiàn)的序列化和反序列化二叉樹算法,結(jié)合實例形式分析了Python二叉樹的構(gòu)造、遍歷、序列化、反序列化等相關(guān)操作技巧,需要的朋友可以參考下2019-03-03