• Python实现网络图形化界面多人聊天室


    Python实现网络图形化界面多人聊天室 - Windows

    项目名称:网络多人聊天室图形界面版本
    项目思路:
        server.py
            服务端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击开始进入聊天室。
            创建子进程,开始网络连接,使用select.select循环接收客户端连接请求,使用timeout不断检查与主进程间队列(multiprocessing.Queues)的情况
        client.py
            客户端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击开始以连接到客户端。
            创建子进程,开始网络连接。
        settings.py
            默认设置
    readme.txt
    # settings.py
    # 默认设置
    
    HOST = "127.0.0.1"
    PORT = 5555
    ADDR = HOST, PORT
    
    def center(root, width=300, height=150):
        # 设置窗口居中
        screenWidth = root.winfo_screenwidth()
        screenHeight = root.winfo_screenheight()
        x = (screenWidth - width) / 2
        y = (screenHeight - height) / 2
        root.geometry("%dx%d+%d+%d" % (width, height, x, y))
    settings.py
    # server.py
    # 服务端代码
    
    from time import ctime
    from multiprocessing import Process, Queue
    from select import select
    from socket import *
    from settings import *
    from tkinter import *
    from tkinter.scrolledtext import ScrolledText
    from tkinter import messagebox
    
    def main_gui():
        # 主窗口
        root = Tk()
    
        # 设置窗口居中
        center(root)
    
        # 设置窗口其他属性
        root.title("多人聊天室主窗口")
        root.resizable(0, 0)
        root.configure(bg="white")
        # root.iconbitmap("python.ico")
    
        # 添加主机名(HOST)以及端口号(PORT)等输入框
        pad = 10
        Label(root, text="主机名(Host):").grid(row=0, column=0, padx=pad, pady=pad)
        ent_host = Entry(root)
        ent_host.insert(0, HOST)
        ent_host.grid(row=0, column=1, padx=pad, pady=pad)
        Label(root, text="端口号(Port):").grid(row=1, column=0, padx=pad, pady=pad)
        ent_port = Entry(root)
        ent_port.insert(0, PORT)
        ent_port.grid(row=1, column=1, padx=pad, pady=pad)
    
        # 组件列表
        widgets = {
            "ent_host": ent_host,
            "ent_port": ent_port
        }
    
        # 添加确认按钮
        btn_cfm = Button(root, text="新建网络聊天室", command=lambda:validate(root, widgets))
        btn_cfm.grid(rowspan=2, columnspan=2, padx=pad, pady=pad)
    
        # 绑定事件
        root.bind("<Return>", lambda event:validate(root, widgets))
    
        # 主循环事件
        root.mainloop()
    
    def validate(root, widgets):
        # 确认按钮事件,检查是否输入有误
        host, port = widgets["ent_host"].get(), widgets["ent_port"].get()
    
        # 如果端口号不是纯数字
        try:
            port = int(port)
        except:
            messagebox.showerror("错误", "端口号必须为数字!")
            return 
    
        # 弹出错误窗口
        if not host or not port:
            messagebox.showerror("错误", "主机名或端口不可为空!")
            return
    
        # 有效地址
        addr = (host, port)
    
        # 检查是否套接字成功
        try:    
            server = socket(AF_INET, SOCK_STREAM)
            server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
            server.bind(addr)
            server.listen(10)
        except Exception as e:
            messagebox.showerror("错误", f"无法在{addr}上生成套接字!")
            print(e)
            return
        else:
            # 生成两个队列
            # queue负责在子进程中循环get主进程的信息
            # queueu负责在父进程中循环get子进程的消息
            queue = Queue()
            queueu = Queue()
    
            # 生成子进程
            process = Process(target=inet, args=(server, queue, queueu))
            process.daemon = True
            process.start()
    
            # 创建聊天室页面
            chatroom_gui(root, queue, queueu)
    
    def chatroom_gui(r, queue, queueu):
        # 聊天室页面
        r.destroy()
        root = Tk()
    
        # 设置窗口居中
        center(root, 500, 500)
    
        # 最小化窗口
        root.minsize(350, 350)
    
        # 菜单栏
        menubar = Menu(root)
        menubar.add_command(label="新建", command=None)
        menubar.add_command(label="信息", command=None)
        menubar.add_command(label="退出", command=root.destroy)
        root.config(menu=menubar)
    
        # 文本框
        text = ScrolledText(root)
        text.pack(fill=BOTH)
    
        # 输入框
        ent = Entry(root, bg='gray', bd=3)
        ent.pack(fill=BOTH, side=BOTTOM)
        ent.focus_set()
    
        # 绑定事件
        root.bind("<Return>", lambda event:send(ent, queue, text))
    
        # 设置窗口其他属性
        root.title("多人聊天室 - 管理员")
        root.configure(bg="white")
        root.iconbitmap("python.ico")
    
        # 主循环函数
        root.after(1000, recv, root, queueu, text)
    
    def recv(root, queueu, text):
        if not queueu.empty():
            data = queueu.get()
            text.insert(END, data)
        root.after(1000, recv, root, queueu, text)
    
    def send(ent, queue, text):
        now =  ":".join(ctime().split()[3].split(":"))
        data = "[" + now + "] " + "管理员:" + ent.get() + "
    "
        queue.put(data)
        text.insert(END, data)
        ent.delete(0, END)
    
    def inet(server, queue, queueu):
        # 子进程
        rlist = [server]
        wlist = []
        xlist = []
    
        while True:
            # 接收队列信息
            if not queue.empty():
                data = queue.get()
                for conn in rlist:
                    if conn is not server:
                        conn.send(bytes(data, "UTF-8"))
    
            rs, ws, xs = select(rlist, wlist, xlist, 1)
    
            for r in rs:
                if r is server:
                    conn, addr = r.accept()
                    rlist.append(conn)
                else:
                    try:
                        data = r.recv(1024)
                    except:
                        rlist.remove(r)
                    else:
                        queueu.put(data.decode())
                        for conn in rlist:
                            if conn is not server:
                                conn.send(data)
    
    def main():
        # 主进程
        main_gui()
    
    if __name__ == "__main__":
        main()
    server.py
    # client.py
    # 客户端代码
    
    import sys
    from time import sleep, ctime
    from multiprocessing import Process, Queue
    from socket import *
    from settings import *
    from tkinter import *
    from tkinter.scrolledtext import ScrolledText
    from tkinter import messagebox
    
    def main_gui():
        # 主窗口
        root = Tk()
    
        # 设置窗口居中
        center(root, 300, 200)
    
        # 设置窗口其他属性
        root.title("多人聊天室主窗口")
        root.resizable(0, 0)
        root.configure(bg="white")
        root.iconbitmap("python.ico")
    
        # 添加主机名(HOST)以及端口号(PORT)等输入框
        pad = 10
        Label(root, text="主机名(Host):").grid(row=0, column=0, padx=pad, pady=pad)
        ent_host = Entry(root)
        ent_host.insert(0, HOST)
        ent_host.grid(row=0, column=1, padx=pad, pady=pad)
        Label(root, text="端口号(Port):").grid(row=1, column=0, padx=pad, pady=pad)
        ent_port = Entry(root)
        ent_port.insert(0, PORT)
        ent_port.grid(row=1, column=1, padx=pad, pady=pad)
        Label(root, text="用户名(User):").grid(row=2, column=0, padx=pad, pady=pad)
        ent_user = Entry(root)
        ent_user.grid(row=2, column=1, padx=pad, pady=pad)
        ent_user.focus_set()
    
        # 组件列表
        widgets = {
            "ent_host": ent_host,
            "ent_port": ent_port,
            "ent_user": ent_user
        }
    
        # 添加确认按钮
        btn_cfm = Button(root, text="加入目标聊天室", command=lambda:validate(root, widgets))
        btn_cfm.grid(rowspan=2, columnspan=2, padx=pad, pady=pad)
    
        # 绑定事件
        root.bind("<Return>", lambda event:validate(root, widgets))
    
        # 主循环事件
        root.mainloop()
    
    def validate(root, widgets):
        # 确认按钮事件,检查是否输入有误
        host, port, user = widgets["ent_host"].get(), widgets["ent_port"].get(), widgets["ent_user"].get()
    
        # 如果端口号不是纯数字
        try:
            port = int(port)
        except:
            messagebox.showerror("错误", "端口号必须为数字!")
            return 
    
        # 弹出错误窗口
        if not host or not port or not user:
            messagebox.showerror("错误", "主机名或端口或用户名不可为空!")
            return
    
        # 有效地址
        addr = (host, port)
    
        # 检查是否套接字成功
        try:    
            client = socket(AF_INET, SOCK_STREAM)
            client.connect(addr)
        except Exception as e:
            messagebox.showerror("错误", f"无法在{addr}上生成套接字!")
            print(e)
            return
        else:
            # 生成两个队列
            # queue负责在子进程中循环get主进程的信息
            # queueu负责在父进程中循环get子进程的消息
            queue = Queue()
            queueu = Queue()
    
            # 发送成功建立连接信息
            client.send(bytes(f"{user}进入了聊天室。
    ", "UTF-8"))
    
            # 生成子进程
            process = Process(target=inet, args=(client, queue, queueu, user))
            process.daemon = True
            process.start()
    
            # 创建聊天室页面
            chatroom_gui(root, queue, queueu, user)
    
    def chatroom_gui(r, queue, queueu, user):
        # 聊天室页面
        r.destroy()
        root = Tk()
    
        # 设置窗口居中
        center(root, 500, 500)
    
        # 最小化窗口
        root.minsize(350, 350)
    
        # 菜单栏
        menubar = Menu(root)
        menubar.add_command(label="信息", command=None)
        menubar.add_command(label="退出", command=root.destroy)
        root.config(menu=menubar)
    
        # 文本框
        text = ScrolledText(root)
        text.pack(fill=BOTH)
    
        # 输入框
        ent = Entry(root, bg='gray', bd=3)
        ent.pack(fill=BOTH, side=BOTTOM)
        ent.focus_set()
    
        # 绑定事件
        root.bind("<Return>", lambda event:send(ent, queue, user))
    
        # 设置窗口其他属性
        root.title(f"多人聊天室 - {user}")
        root.configure(bg="white")
        # root.iconbitmap("python.ico")
    
        # 设置退出方法
        root.protocol("WM_DELETE_WINDOW", lambda: exit(root, queue, user))
    
        # 主循环函数
        root.after(1000, recv, root, queueu, text)
    
    def exit(root, queue, user):
        data = user + "退出了聊天室。
    "
        queue.put(data)
        sleep(1)
        root.destroy()
        sys.exit(0)
    
    def recv(root, queueu, text):
        if not queueu.empty():
            data = queueu.get()
            if data == 404:
                messagebox.showerror("错误", "服务端关闭了连接!")
                sys.exit(0)
            text.insert(END, data)
        root.after(1000, recv, root, queueu, text)
    
    def send(ent, queue, user):
        now = ":".join(ctime().split()[3].split(":"))
        data = "[" + now + "] " + user + "" + ent.get() + "
    "
        queue.put(data)
        ent.delete(0, END)
    
    def inet(client, queue, queueu, user):
        # 子进程
        client.setblocking(0)
        while True:
            if not queue.empty():
                data = queue.get()
                if data:
                    data = bytes(data, "UTF-8")
                    client.send(data)
            try:
                data = client.recv(1024)
            except BlockingIOError as e:
                continue
            except:
                queueu.put(404)
            else:
                data = data.decode()
                queueu.put(data)
    
    def main():
        # 主进程
        main_gui()
    
    if __name__ == "__main__":
        main()
    client.py

    运行截图:

  • 相关阅读:
    Nginx 使用 GeoIP 模块区分用户地区
    使用nginx转发tcp请求(解决访问内网的腾讯云redis)
    open file cache提升nginx性能
    使用 nginx-http-concat
    使用goaccess对Nginx日志简单分析
    Zookeeper系列一:Zookeeper基础命令操作
    k8s nginx应用-获取客户端访问真实IP
    mysql 备份数据库中的一张表
    ssh命令带密码
    Linux下grep显示前后几行信息
  • 原文地址:https://www.cnblogs.com/noonjuan/p/12078524.html
Copyright © 2020-2023  润新知