当前位置 博文首页 > Python实现网络聊天室的示例代码(支持多人聊天与私聊)

    Python实现网络聊天室的示例代码(支持多人聊天与私聊)

    作者:aaaadioppa 时间:2021-02-05 15:14

    实验名称:

    网络聊天室

    功能:

    i. 掌握利用Socket进行编程的技术
    ii. 掌握多线程技术,保证双方可以同时发送
    iii. 建立聊天工具
    iv. 可以和单人聊天
    v. 可以和多个人同时进行聊天
    vi. 使用图形界面,显示双方的语录
    vii. 程序可以在一定程度上进行错误识别

    概述

    实验通过聊天室可以完成单人或多人之间的聊天通信,功能的实现主要是通过Socket通信来实现。本次实验采用客户端/服务器(C/S)架构模式,通过Python语言来编写服务器端与客户端的程序。运用多线程可完成多点对多点的聊天。
    服务器端程序主要用于接收用户信息,消息接收与转发。
    客户端程序实现用户注册登录,聊天信息显示与信息输入。

    代码解释

    统计当前在线人数,并且将新用户加到用户列表中。
    Serve.py

    在这里插入图片描述

    这是服务器对于聊天服务的实现。

    在这里插入图片描述

    通过继承threading.Thread类而实现多线程,重写run函数。

    在这里插入图片描述

    接受来自客户端的用户名,如果用户名为空,使用用户的IP与端口作为用户名。如果用户名出现重复,则在出现的用户名依此加上后缀“2”、“3”、“4”……

    在这里插入图片描述

    在获取用户名后便会不断地接受用户端发来的消息(即聊天内容),结束后关闭连接。

    在这里插入图片描述

    如果用户断开连接,将该用户从用户列表中删除,然后更新用户列表。

    在这里插入图片描述

    将地址与数据(需发送给客户端)存入messages队列。

    在这里插入图片描述

    服务端在接受到数据后,会对其进行一些处理然后发送给客户端,如下图,对于聊天内容,服务端直接发送给客户端,而对于用户列表,便由json.dumps处理后发送。

    在这里插入图片描述

    Client.py
    建立连接,发送用户名及判断是否为私聊消息,私聊用~识别

    在这里插入图片描述

    接受来自服务器发送的消息

    在这里插入图片描述

    对接收到的消息进行判断,如果是在线用户列表(用json.dumps处理过),便清空在线用户列表框,并将此列表输出在在线用户列表框中。

    在这里插入图片描述

    如果是聊天内容,便将其输出在聊天内容显示框中。

    在这里插入图片描述

    设置登录窗口

    在这里插入图片描述

    设置消息界面

    在这里插入图片描述

    设置在线用户列表。

    在这里插入图片描述

    完整代码:
    Serve.py

    import socket
    import threading
    import queue
    import json # json.dumps(some)打包  json.loads(some)解包
    import os
    import os.path
    import sys
    
    
    IP = '127.0.0.1'
    PORT = 9999   # 端口
    messages = queue.Queue()
    users = []  # 0:userName 1:connection
    lock = threading.Lock()
    
    def onlines():  # 统计当前在线人员
      online = []
      for i in range(len(users)):
        online.append(users[i][0])
      return online
    
    class ChatServer(threading.Thread):
      global users, que, lock
    
      def __init__(self):     # 构造函数
        threading.Thread.__init__(self)
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        os.chdir(sys.path[0])
    # 接受来自客户端的用户名,如果用户名为空,使用用户的IP与端口作为用户名。如果用户名出现重复,则在出现的用户名依此加上后缀“2”、“3”、“4”……
      def receive(self, conn, addr):       # 接收消息
        user = conn.recv(1024)    # 用户名称
        user = user.decode()
        if user == '用户名不存在':
          user = addr[0] + ':' + str(addr[1])
        tag = 1
        temp = user
        for i in range(len(users)):   # 检验重名,则在重名用户后加数字
          if users[i][0] == user:
            tag = tag + 1
            user = temp + str(tag)
        users.append((user, conn))
        USERS = onlines()
        self.Load(USERS,addr)
        # 在获取用户名后便会不断地接受用户端发来的消息(即聊天内容),结束后关闭连接。
        try:
          while True:
            message = conn.recv(1024)      # 发送消息
            message = message.decode()
            message = user + ':' + message
            self.Load(message,addr)
          conn.close()
        # 如果用户断开连接,将该用户从用户列表中删除,然后更新用户列表。
        except:  
          j = 0      # 用户断开连接
          for man in users:
            if man[0] == user:
              users.pop(j)    # 服务器段删除退出的用户
              break
            j = j+1
    
          USERS = onlines()
          self.Load(USERS,addr)
          conn.close()
    
    # 将地址与数据(需发送给客户端)存入messages队列。
      def Load(self, data, addr):
        lock.acquire()
        try:
          messages.put((addr, data))
        finally:
          lock.release()    
    
      # 服务端在接受到数据后,会对其进行一些处理然后发送给客户端,如下图,对于聊天内容,服务端直接发送给客户端,而对于用户列表,便由json.dumps处理后发送。
      def sendData(self): # 发送数据
        while True:
          if not messages.empty():
            message = messages.get()
            if isinstance(message[1], str):
              for i in range(len(users)):
                data = ' ' + message[1]
                users[i][1].send(data.encode())
                print(data)
                print('\n')
    
            if isinstance(message[1], list):
              data = json.dumps(message[1])
              for i in range(len(users)):
                try:
                  users[i][1].send(data.encode())
                except:
                  pass
    
      def run(self):
        self.s.bind((IP,PORT))
        self.s.listen(5)
        q = threading.Thread(target=self.sendData)
        q.start()
        while True:
          conn, addr = self.s.accept()
          t = threading.Thread(target=self.receive, args=(conn, addr))
          t.start()
        self.s.close()
    if __name__ == '__main__':
      cserver = ChatServer()
    cserver.start()
    
    

    Client.py

    import socket
    import tkinter
    import tkinter.messagebox
    import threading
    import json
    import tkinter.filedialog
    from tkinter.scrolledtext import ScrolledText
    
    IP = ''
    PORT = ''
    user = ''
    listbox1 = '' # 用于显示在线用户的列表框
    show = 1 # 用于判断是开还是关闭列表框
    users = [] # 在线用户列表
    chat = '------Group chat-------' # 聊天对象
    
    #登陆窗口
    
    root0 = tkinter.Tk()
    root0.geometry("300x150")
    root0.title('用户登陆窗口')
    root0.resizable(0,0)
    one = tkinter.Label(root0,width=300,height=150,bg="LightBlue")
    one.pack()
    
    IP0 = tkinter.StringVar()
    IP0.set('')
    USER = tkinter.StringVar()
    USER.set('')
    
    labelIP = tkinter.Label(root0,text='IP地址',bg="LightBlue")
    labelIP.place(x=20,y=20,width=100,height=40)
    entryIP = tkinter.Entry(root0, width=60, textvariable=IP0)
    entryIP.place(x=120,y=25,width=100,height=30)
    
    labelUSER = tkinter.Label(root0,text='用户名',bg="LightBlue")
    labelUSER.place(x=20,y=70,width=100,height=40)
    entryUSER = tkinter.Entry(root0, width=60, textvariable=USER)
    entryUSER.place(x=120,y=75,width=100,height=30)
    
    def Login(*args):
    	global IP, PORT, user
    	IP, PORT = entryIP.get().split(':')
    	user = entryUSER.get()
    	if not user:
    		tkinter.messagebox.showwarning('warning', message='用户名为空!')
    	else:
    		root0.destroy()
    
    loginButton = tkinter.Button(root0, text ="登录", command = Login,bg="Yellow")
    loginButton.place(x=135,y=110,width=40,height=25)
    root0.bind('<Return>', Login)
    
    root0.mainloop()
    
    # 建立连接
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((IP, int(PORT)))
    if user:
      s.send(user.encode()) # 发送用户名
    else:
      s.send('用户名不存在'.encode())
      user = IP + ':' + PORT
    
    # 聊天窗口
    root1 = tkinter.Tk()
    root1.geometry("640x480")
    root1.title('群聊')
    root1.resizable(0,0)
    
    # 消息界面
    listbox = ScrolledText(root1)
    listbox.place(x=5, y=0, width=640, height=320)
    listbox.tag_config('tag1', foreground='red',backgroun="yellow")
    listbox.insert(tkinter.END, '欢迎进入群聊,大家开始聊天吧!', 'tag1')
    
    INPUT = tkinter.StringVar()
    INPUT.set('')
    entryIuput = tkinter.Entry(root1, width=120, textvariable=INPUT)
    entryIuput.place(x=5,y=320,width=580,height=170)
    
    # 在线用户列表
    listbox1 = tkinter.Listbox(root1)
    listbox1.place(x=510, y=0, width=130, height=320)
    
    
    def send(*args):
    	message = entryIuput.get() + '~' + user + '~' + chat
    	s.send(message.encode())
    	INPUT.set('')
    
    sendButton = tkinter.Button(root1, text ="\n发\n\n\n送",anchor = 'n',command = send,font=('Helvetica', 18),bg = 'white')
    sendButton.place(x=585,y=320,width=55,height=300)
    root1.bind('<Return>', send)
    
    
    def receive():
    	global uses
    	while True:
    		data = s.recv(1024)
    		data = data.decode()
    		print(data)
    		try:
    			uses = json.loads(data)
    			listbox1.delete(0, tkinter.END)
    			listbox1.insert(tkinter.END, "当前在线用户")
    			listbox1.insert(tkinter.END, "------Group chat-------")
    			for x in range(len(uses)):
    				listbox1.insert(tkinter.END, uses[x])
    			users.append('------Group chat-------')
    		except:
    			data = data.split('~')
    			message = data[0]
    			userName = data[1]
    			chatwith = data[2]
    			message = '\n' + message
    			if chatwith == '------Group chat-------':  # 群聊
    				if userName == user:
    					listbox.insert(tkinter.END, message)
    				else:
    					listbox.insert(tkinter.END, message)
    			elif userName == user or chatwith == user: # 私聊
    				if userName == user:
    					listbox.tag_config('tag2', foreground='red')
    					listbox.insert(tkinter.END, message, 'tag2')
    				else:
    					listbox.tag_config('tag3', foreground='green')
    					listbox.insert(tkinter.END, message,'tag3')
    
    			listbox.see(tkinter.END)
    r = threading.Thread(target=receive)
    r.start() # 开始线程接收信息
    
    root1.mainloop()
    s.close()
    
    
    js