基于python实现聊天室程序
本文实例为大家分享了python实现简单聊天室的具体代码,供大家参考,具体内容如下
刚刚接触python编程,又从接触java开始一直对socket模块感兴趣,所以就做了一个聊天室的小程序。
该程序由客户端与服务器构成,使用UDP服务,服务器端绑定本地IP和端口,客户端由系统随机选择端口。
实现了群发、私发、点对点文件互传功能。
客户端自建了一个类继承了Cmd模块,使用自定义的命令command进行操作,调用相应的do_command方法。
使用json模块进行消息的封装序列化,在接收方进行解析。
客户端代码如下:
importsocket importthreading importjson importos fromcmdimportCmd classClient(Cmd): """ 客户端 """ prompt='>>>' intro='[Welcome]简易聊天室客户端(Cli版)\n'+'[Welcome]输入help来获取帮助\n' buffersize=1024 def__init__(self,host): """ 构造 """ 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 #是否在发送文件 self.sendfile=False self.filesize=None self.sendfilesize=None #接收文件包计数 self.filecount=None #接收文件名 self.filename=None #发送文件名 self.sendfilename=None #发送者 self.filefrom=None #接收者 self.fileto=None #接收文件流 self.file_recv=None #发送文件流 self.file_send=None #接收文件地址 self.filefrom_addr=None #发送文件地址 self.fileto_addr=None def__receive_message_thread(self): """ 接受消息线程 """ whileself.threadisalive: #noinspectionPyBroadException try: buffer,addr=self.__socket.recvfrom(1024) ''' 文件流由发送端直接发送,不经过服务器,故当发送端发来的消息时,将收到的数据存入文件 ''' if(addr!=self.__host)&(addr==self.filefrom_addr)&self.recvfile: self.file_recv.write(buffer) self.filecount+=1 ifself.filecount*1024>=self.filesize: self.file_recv.close() print(self.filename,'isreceived.') self.recvfile=False continue js=json.loads(buffer.decode()) #若接收的数据为消息信息,则显示 ifjs['type']=='message': print(js['message']) #若接收的数据为文件发送请求,则存储文件信息,并显示 elifjs['type']=='filequest': ifself.recvfile: self.__socket.sendto(json.dumps({ 'type':'fileres', 'fileres':'no', 'nickname':self.__nickname, 'who':js['nickname'], 'errormessage':'istransfromingfiles.', }).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,'sendafile(', filename,')toyou.receive?') #接受的数据为请求回复,若同意接收则存储服务器发来的接收方的地址,并开启发送线程 elifjs['type']=='fileres': ifjs['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 exceptExceptionase: print(e) print('[Client]无法从服务器获取数据') def__send_broadcast_message_thread(self,message): """ 发送广播消息线程 :parammessage:消息内容 """ self.__socket.sendto(json.dumps({ 'type':'broadcast', 'nickname':self.__nickname, 'message':message, }).encode(),self.__host) def__send_file_thread(self): """ 发送文件线程 :parammessage:消息内容 """ filecount=0 print('[system]','sendingthefile...') whilefilecount*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]','thefileissended.') def__send_whisper_message_thread(self,who,message): """ 发送私发消息线程 :parammessage:消息内容 """ self.__socket.sendto(json.dumps({ 'type':'sendto', 'who':who, 'nickname':self.__nickname, 'message':message }).encode(),self.__host) defsend_exit(self): self.__socket.sendto(json.dumps({ 'type':'offline', 'nickname':self.__nickname, }).encode(),self.__host) defstart(self): """ 启动客户端 """ self.cmdloop() defdo_login(self,args): """ 登录聊天室 :paramargs:参数 """ nickname=args.split('')[0] #将昵称发送给服务器,获取用户id self.__socket.sendto(json.dumps({ 'type':'login', 'nickname':nickname, }).encode(),self.__host) #尝试接受数据 buffer=self.__socket.recvfrom(1300)[0].decode() obj=json.loads(buffer) ifobj['login']=='success': self.__nickname=nickname print('[Client]成功登录到聊天室') self.threadisalive=True #开启子线程用于接受数据 self.thread_recv=threading.Thread( target=self.__receive_message_thread) self.thread_recv.setDaemon(True) self.thread_recv.start() else: print('[Client]无法登录到聊天室',obj['errormessage']) defdo_send(self,args): """ 发送消息 :paramargs:参数 """ ifself.__nicknameisNone: print('请先登录!loginnickname') return message=args #开启子线程用于发送数据 thread=threading.Thread( target=self.__send_broadcast_message_thread,args=(message,)) thread.setDaemon(True) thread.start() defdo_sendto(self,args): """ 发送私发消息 :paramargs:参数 """ ifself.__nicknameisNone: print('请先登录!loginnickname') return who=args.split('')[0] message=args.split('')[1] ##显示自己发送的消息 #print('['+str(self.__nickname)+'('+str(self.__id)+')'+']',message) #开启子线程用于发送数据 thread=threading.Thread( target=self.__send_whisper_message_thread,args=(who,message)) thread.setDaemon(True) thread.start() defdo_catusers(self,arg): ifself.__nicknameisNone: print('请先登录!loginnickname') return catmessage=json.dumps({'type':'catusers'}) self.__socket.sendto(catmessage.encode(),self.__host) defdo_catip(self,args): ifself.__nicknameisNone: print('请先登录!loginnickname') return who=args catipmessage=json.dumps({'type':'catip','who':who}) self.__socket.sendto(catipmessage.encode(),self.__host) defdo_help(self,arg): """ 帮助 :paramarg:参数 """ command=arg.split('')[0] ifcommand=='': print('[Help]loginnickname-登录到聊天室,nickname是你选择的昵称') print('[Help]sendmessage-发送消息,message是你输入的消息') print('[Help]sendtowhomessage-私发消息,who是用户名,message是你输入的消息') print('[Help]catusers-查看所有用户') print('[Help]catipwho-查看用户IP,who为用户名') print('[Help]sendfilewhofiledir-向某用户发送文件,who为用户名,filedir为文件路径') print('[Help]getfilefilenamewhoyes/no-接收文件,filename为文件名,who为发送者,yes/no为是否接收') elifcommand=='login': print('[Help]loginnickname-登录到聊天室,nickname是你选择的昵称') elifcommand=='send': print('[Help]sendmessage-发送消息,message是你输入的消息') elifcommand=='sendto': print('[Help]sendtowhomessage-发送私发消息,message是你输入的消息') else: print('[Help]没有查询到你想要了解的指令') defdo_exit(self,arg):#以do_*开头为命令 print("Exit") self.send_exit() try: self.threadisalive=False self.thread_recv.join() exceptExceptionase: print(e) #self.__socket.close() defdo_sendfile(self,args): who=args.split('')[0] filepath=args.split('')[1] filename=filepath.split('\\')[-1] #判断是否在发送文件 ifself.sendfile: print('youaresendingfiles,pleasetrylater.') return ifnotos.path.exists(filepath): print('thefileisnotexist.') 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('requestsend...') #fileres=self.__socket.recvfrom(1024)[0].decode() #js=json.loads(fileres) defdo_getfile(self,args): filename=args.split('')[0] who=args.split('')[1] ch=args.split('')[2] #print(self.filenameisnotNone,filename,self.filename,who,self.filefrom) if(self.filenameisnotNone)&(filename==self.filename)&(who==self.filefrom): ifch=='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('youagreetoreveivethefile(',filename,')from',who) else: self.__socket.sendto(json.dumps({ 'type':'fileres', 'fileres':'no', 'nickname':self.__nickname, 'errormessage':'denythefile.', 'who':who, 'recv_ip':'', 'recv_port':'', }).encode(),self.__host) print('youdenytoreveivethefile(',filename,')from',who) self.recvfile=False else: print('thenameorsenderofthefileiswrong.') c=Client(('127.0.0.1',12346)) c.start()
服务器端主要进行消息的分类转发处理,用户列表、地址列表的维护。
服务器端代码如下:
importsocket importjson obj=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) obj.bind(('127.0.0.1',12346)) conn_list=[] user_list=[] whileTrue: try: receive_data,client_address=obj.recvfrom(1024) js=json.loads(receive_data.decode()) #登录消息 ifjs['type']=='login': nickname=str(js['nickname']) ifnicknameinuser_list: obj.sendto(json.dumps({'login':'fail', 'errormessage':'thenicknameisexists'}).encode(), client_address) else: #向其他用户发送通知 foriinrange(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) #群发消息 elifjs['type']=='broadcast': message=js['message'] nickname=js['nickname'] foriinrange(len(conn_list)): obj.sendto(json.dumps( { 'type':'message', 'message':nickname+':'+message }).encode(),conn_list[i]) #私发消息 elifjs['type']=='sendto': who=js['who'] nickname=js['nickname'] message=js['message'] #检查用户是否存在 ifwhonotinuser_list: obj.sendto(json.dumps( { 'type':'message', 'message':who+'notexistornotonline.pleasetrylater.' }).encode(), client_address) else: obj.sendto(json.dumps( { 'type':'message', 'message':nickname+'whispertoyou:'+message }).encode(), conn_list[user_list.index(who)]) #查看用户列表 elifjs['type']=='catusers': users=json.dumps(user_list) obj.sendto(json.dumps( { 'type':'message', 'message':users, }).encode(), client_address) #查看用户IP elifjs['type']=='catip': who=js['who'] ifwhonotinuser_list: obj.sendto(json.dumps( { 'type':'message', 'message':who+'notexistornotonline.pleasetrylater.' }).encode(), client_address) else: addr=json.dumps(conn_list[user_list.index(who)]) obj.sendto(json.dumps( { 'type':'message', 'message':addr, }).encode(), client_address) #离线消息 elifjs['type']=='offline': user_list.remove(js['nickname']) conn_list.remove(client_address) obj.sendto( (js['nickname']+'offline.').encode(), client_address) #向其他用户发送通知 foriinrange(len(conn_list)): obj.sendto(json.dumps( { 'type':'message', 'message':'[system]'+nickname+'下线了.' }).encode(),conn_list[i]) #发送文件请求 elifjs['type']=='filequest': who=js['who'] ifwhonotinuser_list: obj.sendto(json.dumps( { 'type':'message', 'message':who+'notexistornotonline.pleasetrylater.' }).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'],'requesttosendfileto',who) #发送文件请求回复 elifjs['type']=='fileres': who=js['who'] ifjs['fileres']=='yes': js['recv_ip']=client_address[0] js['recv_port']=client_address[1] print(js['nickname'],'agreetoreceivefilefrom',js['who']) else: print(js['nickname'],'denytoreceivefilefrom',js['who']) obj.sendto(json.dumps(js).encode(), conn_list[user_list.index(who)]) exceptExceptionase: print(e)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。