python学习项目 多会话线程的多人聊天室

 服务器端

import wx
from socket import socket
from socket import AF_INET#AF_INET 用于internet之间的进程通信
from socket import SOCK_STREAM#SOCK_STREAM 表示使用TCP协议
import threading
import time

class Server(wx.Frame):
    def __init__(self):
        #界面绘制
        # 调用父类初始化方法
        wx.Frame.__init__(self, None, id=1001, title='多人聊天室服务器端界面', pos=wx.DefaultPosition,size=(450, 450))  # None 表没有父级窗口 id 表当前窗口编号 pos 表窗口打开位置 size 表窗体大小,单位是像素
        panel = wx.Panel(self)  # 创建面板对象
        box = wx.BoxSizer(wx.VERTICAL)  # 在面板中放入盒子 wx.VERTICAL 表垂直方向布局

        gridding1 = wx.FlexGridSizer(wx.HSCROLL)  # 创建可伸缩的网格布局1 wx.HSCROLL 表水平方向布局
        start_button = wx.Button(panel, size=(150, 40), label='启动服务器')  # 创建按钮对象于面板上,并标记上文字
        save_button = wx.Button(panel, size=(150, 40), label='保存聊天记录')
        stop_button = wx.Button(panel, size=(150, 40), label='关断服务器')
        gridding1.Add(start_button, 1, wx.TOP)  # 把按钮顶行放到可伸缩的网格中
        gridding1.Add(save_button, 1, wx.TOP)
        gridding1.Add(stop_button, 1, wx.TOP)
        box.Add(gridding1, 1, wx.ALIGN_CENTRE)  # 网格布局1添加至box中 wx.ALIGN_CENTR表居中对齐

        self.show_text = wx.TextCtrl(panel, size=(450, 410),style=wx.TE_MULTILINE | wx.TE_READONLY)  # 创建只读文本框,用于显示聊天内容 wx.TE_MULTILINE 表多行文本框 wx.TE_READONLY 表只读
        box.Add(self.show_text, 1, wx.ALIGN_CENTRE)  # 添加文本框于box中

        panel.SetSizer(box)  # 放置box于面板中

        #功能属性
        self.ison=False#存储服务器启动状态,默认为未启动状态(False)
        self.ip_port=('',8888)#设置服务器端绑定的ip地址和端口(元组类型) '' 空字符串表本机所有ip地址
        self.server_socket=socket(AF_INET,SOCK_STREAM)#创建socket对象
        self.server_socket.bind(self.ip_port)#绑定ip地址和端口
        self.server_socket.listen(5)#监听
        self.chat_thread_dictionary={}#创建字典,用于存储客户端对话线程 key(客户端名称)-value(会话线程)

        #按钮绑定方法
        self.Bind(wx.EVT_BUTTON, self.start, start_button)
        self.Bind(wx.EVT_BUTTON, self.save, save_button)
        self.Bind(wx.EVT_BUTTON, self.stop, stop_button)

    #方法
    def start(self, event):#event 表按钮被按动这一事件
        if not self.ison:#判断服务器启动状态是否为未启动状态
            self.ison=True
            main_thread=threading.Thread(target=self.turn_on)#创建主线程对象
            main_thread.daemon=True#main_thread设置为守护线程,父线程(界面)执行结束,子线程跟着执行结束
            main_thread.start()
        print("服务器启动")
    def turn_on(self):
        while self.ison:#服务器是启动状态时运行
            chat_socket,client_address=self.server_socket.accept()#等待客户的连接 系列解包赋值客户端的套接字和地址信息
            #客户发送连接请求后,客户发送的第一挑数据作客户端名称,客户端名称作字典中的key
            client_name=chat_socket.recv(1024).decode('utf-8')#接收解码客户端数据
            chat_thread=Chatthread(chat_socket,client_name,self)#创建会话线程对象
            self.chat_thread_dictionary[client_name]=chat_thread#存储到字典中
            chat_thread.start()#启动会话线程
            self.show_chat('聊天室通知',f'{client_name}进入聊天室',time.strftime('%Y-%m-%d\t%H:%M:%S',time.localtime()))#输出服务器提示信息
        self.server_socket.close()#服务器是关闭状态时,关闭服务器端socket
    def show_chat(self,chat_source,chat,chattime):#聊天信息源 聊天信息 聊天时间
        send_chat=f'{chat_source}:{chat}\n时间:{chattime}'
        self.show_text.AppendText('-'*40+'\n'+send_chat+'\n')#AppendText()格式化显示聊天信息于服务器端界面只读文本框
        for client in self.chat_thread_dictionary.values():#将聊天信息发送至每一位在线客户会话线程
            if client.ison:#需连接,才能发送聊天信息
                client.chat_socket.send(send_chat.encode('utf-8'))
    def save(self,event):#event 表按钮被按动这一事件
        save_chat=self.show_text.GetValue()#GetValue()保存信息
        with open('save_chat.log','w',encoding='utf-8')as file:
            file.write(save_chat)
    def stop(self,event):#event 表按钮被按动这一事件
        print("聊天室关闭")
        self.ison=False

class Chatthread(threading.Thread):#创建会话线程类
    def __init__(self,chat_socket,client_name,server):#客户端的套接字 客户端名称 服务器
        #调用父类初始化方法
        threading.Thread.__init__(self)
        self.chat_socket=chat_socket
        self.client_name=client_name
        self.server=server
        self.ison=True#创建会话线程对象时,服务器是启动状态

    def run(self):#重写run方法
        print(f'{self.client_name}进入聊天室')
        while self.ison:#服务器是启动状态时,从客户端接收数据
            chat=self.chat_socket.recv(1024).decode('utf-8')#接收解码数据,存储到chat中
            if chat==f'{self.client_name}退出聊天室':#自定义关键词判断客户端是否选择退出聊天室
                self.ison=False
                self.server.show_chat('聊天室通知',f'{self.client_name}退出聊天室',time.strftime('%Y-%m-%d\t%H:%M:%S',time.localtime()))#发送用户退出通知
            else:#正常聊天内容,可读于所有客户端与服务器端
                self.server.show_chat(self.client_name,chat,time.strftime('%Y-%m-%d\t%H:%M:%S',time.localtime()))
        self.chat_socket.close()#客户端选择退出聊天室,关闭客户端socket

if __name__ == '__main__':
    app=wx.App()
    Server().Show()#创建服务器端界面对象 打点调用Show方法
    app.MainLoop()#循环刷新显示

客户端

import wx#图形化界面
from socket import socket
from socket import AF_INET#AF_INET 用于internet之间的进程通信
from socket import SOCK_STREAM#SOCK_STREAM 表示使用TCP协议
import threading

class Client(wx.Frame):
    def __init__(self,client_name):
        # 界面绘制
        #调用父类初始化方法
        wx.Frame.__init__(self,None,id=1001,title=client_name+' 客户端界面',pos=wx.DefaultPosition,size=(400,450))#None 表没有父级窗口 id 表当前窗口编号 pos 表窗口打开位置 size 表窗体大小,单位是像素
        panel=wx.Panel(self)#创建面板对象
        box=wx.BoxSizer(wx.VERTICAL)#在面板中放入盒子 wx.VERTICAL 表垂直方向布局

        gridding1=wx.FlexGridSizer(wx.HSCROLL)#创建可伸缩的网格布局1 wx.HSCROLL 表水平方向布局
        connect_button=wx.Button(panel,size=(200,40),label='连接')#创建按钮对象于面板上,并标记上文字
        disconnect_button = wx.Button(panel, size=(200, 40), label='断开')
        gridding1.Add(connect_button,1,wx.TOP | wx.LEFT)#把两按钮放到可伸缩的网格中 放置顶行左
        gridding1.Add(disconnect_button, 1, wx.TOP | wx.RIGHT)#放置顶行右
        box.Add(gridding1,1,wx.ALIGN_CENTRE)#网格布局1添加至box中 wx.ALIGN_CENTR表居中对齐

        self.show_text=wx.TextCtrl(panel,size=(400,210),style=wx.TE_MULTILINE | wx.TE_READONLY)#创建只读文本框,用于显示聊天内容 wx.TE_MULTILINE 表多行文本框 wx.TE_READONLY 表只读
        box.Add(self.show_text,1,wx.ALIGN_CENTRE)#添加文本框于box中
        self.chat_text=wx.TextCtrl(panel,size=(400,120),style=wx.TE_MULTILINE)#创建写入文本框,用于写入聊天内容
        box.Add(self.chat_text, 1, wx.ALIGN_CENTRE)

        gridding2 = wx.FlexGridSizer(wx.HSCROLL)#创建可伸缩的网格布局2
        reset_button = wx.Button(panel, size=(200, 40), label='清空')  # 创建按钮对象于面板上,并标记上文字
        send_button = wx.Button(panel, size=(200, 40), label='发送')
        gridding2.Add(reset_button, 1, wx.TOP | wx.LEFT)  # 把两按钮放到可伸缩的网格中 放置顶行左边
        gridding2.Add(send_button, 1, wx.TOP | wx.RIGHT)  # 放置顶行右边
        box.Add(gridding2,1,wx.ALIGN_CENTRE)#网格布局2添加至box中

        panel.SetSizer(box)#放置box于面板中

        # 功能属性
        self.client_name=client_name
        self.isconnect=False#存储连接状态,默认为未连接状态(False)
        self.client_socket=None#设置客户端的socket对象为空

        # 按钮绑定方法
        self.Bind(wx.EVT_BUTTON, self.connect, connect_button)
        self.Bind(wx.EVT_BUTTON, self.disconnect, disconnect_button)
        self.Bind(wx.EVT_BUTTON, self.send_chat, send_button)
        self.Bind(wx.EVT_BUTTON, self.reset_chat, reset_button)

    # 方法
    def connect(self, event):#event 表按钮被按动这一事件
        if not self.isconnect:#判断是否是为未连接状态
            serve_id_port=('127.0.0.1',8888)
            self.client_socket=socket(AF_INET,SOCK_STREAM)#创建socket对象
            self.client_socket.connect(serve_id_port)#请求连接
            self.isconnect=True
            self.client_socket.send(self.client_name.encode('utf-8'))
            chat_thread=threading.Thread(target=self.receive_data)##创建chat线程对象
            chat_thread.daemon=True#chat_thread设置为守护线程,父线程(界面)执行结束,子线程跟着执行结束
            chat_thread.start()
        print(f'{self.client_name} 连接服务器')
    def receive_data(self):
        while self.isconnect:#需连接,才能接收服务器传来信息
            chat=self.client_socket.recv(1024).decode('utf-8')
            self.show_text.AppendText('-'*40+'\n'+chat+'\n')#AppendText()格式化显示聊天信息于只读文本框
    def disconnect(self,event):#event 表按钮被按动这一事件
        self.client_socket.send(f'{self.client_name}退出聊天室'.encode('utf-8'))#发送关键词退出
        self.isconnect=False#改变连接状态
    def send_chat(self, event):#event 表按钮被按动这一事件
        if self.isconnect:#需连接,才能发送信息
            input_chat=self.chat_text.GetValue()#GetValue()从文本框读取数据
            if input_chat!='':
                self.client_socket.send(input_chat.encode('utf-8'))#发送信息
                self.chat_text.SetValue('')#SetValue()清空文本框
    def reset_chat(self,event):#event 表按钮被按动这一事件
        self.chat_text.Clear()#Clear()清空文本框

if __name__ == '__main__':
    app=wx.App()
    client1=Client('用户1')#创建客户端界面对象
    client2=Client('用户2')
    #...
    client1.Show()#打点调用Show方法
    client2.Show()
    #...
    app.MainLoop()#循环刷新显示

运行示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值