From 51d41d9ffc752ee824b0e19cf900dd021d3ae941 Mon Sep 17 00:00:00 2001 From: lsy2246 Date: Wed, 3 Apr 2024 02:56:34 +0800 Subject: [PATCH] =?UTF-8?q?client=E6=97=A0=E6=B3=95=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=90=8E=E9=9D=A2=E7=9A=84=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code/chat_room/Chat_Room_First/client.py | 105 +++++++++++++ .../code/chat_room/Chat_Room_First/server.py | 146 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 python/code/chat_room/Chat_Room_First/client.py create mode 100644 python/code/chat_room/Chat_Room_First/server.py diff --git a/python/code/chat_room/Chat_Room_First/client.py b/python/code/chat_room/Chat_Room_First/client.py new file mode 100644 index 0000000..a30f7ed --- /dev/null +++ b/python/code/chat_room/Chat_Room_First/client.py @@ -0,0 +1,105 @@ +import threading +from multiprocessing import Queue +from socket import AF_INET, SOCK_STREAM, socket +from threading import Thread +import wx + + +class MyFrame(wx.Frame): + def __init__(self, client_name): + # 调用父类的初始化方法 + # None没有父级窗口 + # id:表示当前窗口的一个编号 + # pos:窗口打开的位置 + # size:窗口的大小,单位是像素,400宽,450高 + wx.Frame.__init__(self, None, id=7878, title=client_name + '的客户端', pos=wx.DefaultPosition, size=(400, 500)) + + # 创建面板对象 + panel = wx.Panel(self) + + # 在面板上放上盒子 + box = wx.BoxSizer(wx.VERTICAL) # 垂直方向 + + # 可伸缩网格 + grid1 = wx.FlexGridSizer(wx.HSCROLL) # 方向水平布局 + # 创建两个按钮 + conn_link = wx.Button(panel, size=(200, 40), label="连接") + conn_link_dis = wx.Button(panel, size=(200, 40), label="断开") + # 把按钮放在可伸缩网格 + grid1.Add(conn_link, 0, wx.TOP | wx.LEFT) + grid1.Add(conn_link_dis, 0, wx.TOP | wx.RIGHT) + # 可伸缩网格添加到盒子 + box.Add(grid1, 1, wx.ALIGN_CENTER) + + # 只读文本框,显示聊天内容 + self.show_text = wx.TextCtrl(panel, size=(400, 210), style=wx.TE_MULTILINE | wx.TE_READONLY) + box.Add(self.show_text, 1, wx.ALIGN_CENTER) + + # 创建聊天内容 + self.chat_text = wx.TextCtrl(panel, size=(400, 120), style=wx.TE_MULTILINE) + box.Add(self.chat_text, 1, wx.ALIGN_CENTER) + + # 可伸缩网格 + grid2 = wx.FlexGridSizer(wx.HSCROLL) # 方向水平布局 + # 创建两个按钮 + conn_reset = wx.Button(panel, size=(200, 40), label="重置") + conn_send = wx.Button(panel, size=(200, 40), label="发送") + # 把按钮放在可伸缩网格 + grid2.Add(conn_reset, 0, wx.TOP | wx.LEFT) + grid2.Add(conn_send, 0, wx.TOP | wx.RIGHT) + # 可伸缩网格添加到盒子 + box.Add(grid2, 1, wx.ALIGN_CENTER) + + panel.SetSizer(box) + + """—————————按钮绑定操作—————————""" + self.Bind(wx.EVT_BUTTON, self.link_server, conn_link) + self.client_name = client_name + self.state = False + self.server_socket = None + self.server_host_port = ('localhost', 8787) # 空代表的是本机IP + + self.Bind(wx.EVT_BUTTON, self.send_data, conn_send) + self.Bind(wx.EVT_BUTTON, self.stop_server, conn_link_dis) + self.Bind(wx.EVT_BUTTON, self.reset_screen, conn_reset) + + def link_server(self, event): + if not self.state: + self.server_socket = socket(AF_INET, SOCK_STREAM) + self.server_socket.connect(self.server_host_port) + self.server_socket.send(self.client_name.encode('utf-8')) + recv_thread = threading.Thread(target=self.recv_data) + recv_thread.daemon = True + self.state = True + recv_thread.start() + + def stop_server(self, event): + if self.state: + self.server_socket.send('client-exit'.encode('utf-8')) + self.show_text.AppendText('-' * 40 + '\n' + "成功与服务器断开连接" + '\n') + self.state = False + + def reset_screen(self, event): + self.show_text.Clear() + + def recv_data(self): + if self.state: + show_info = self.server_socket.recv(1024).decode('utf-8') + self.show_text.AppendText('-' * 40 + '\n' + show_info + '\n') + + def send_data(self, event): + if self.state: + input_data = self.chat_text.GetValue() + if input_data != "": + self.server_socket.send(input_data.encode('utf-8')) + self.chat_text.SetValue('') + + +if __name__ == '__main__': + # 初始化app() + app = wx.App() + # 创建直接的客户端对象 + frame = MyFrame("lsy").Show() + + # 循环刷新显示 + app.MainLoop() diff --git a/python/code/chat_room/Chat_Room_First/server.py b/python/code/chat_room/Chat_Room_First/server.py new file mode 100644 index 0000000..ec3aa5a --- /dev/null +++ b/python/code/chat_room/Chat_Room_First/server.py @@ -0,0 +1,146 @@ +import threading +import time +from socket import AF_INET, SOCK_STREAM, socket +from multiprocessing import Queue +from threading import Thread +import wx +import os + +class Myserver(wx.Frame): + def __init__(self): + wx.Frame.__init__(self, None, id=8787, title="Server", pos=wx.DefaultPosition, size=(400, 500)) + + panel = wx.Panel(self) + + box = wx.BoxSizer(wx.VERTICAL) + + grid = wx.FlexGridSizer(wx.HSCROLL) + + conn_start = wx.Button(panel, size=(133, 40), label="启动") + conn_save = wx.Button(panel, size=(133, 40), label="保存") + conn_stop = wx.Button(panel, size=(133, 40), label="停止") + + grid.Add(conn_start, 0, wx.TOP) + grid.Add(conn_save, 0, wx.TOP) + grid.Add(conn_stop, 0, wx.TOP) + + box.Add(grid, 0, wx.ALIGN_CENTER) + + self.show_text = wx.TextCtrl(panel, size=(400, 450), style=wx.TE_MULTILINE | wx.TE_READONLY) + box.Add(self.show_text, 0, wx.ALIGN_CENTER) + + panel.SetSizer(box) + + """—————————服务器的必要属性—————————""" + self.state = False # 储存服务器的启动状态,默认值为False + self.host_port = ('localhost', 8787) # 空代表的是本机IP + # 创建socket对象 + self.server_socket = socket(AF_INET, SOCK_STREAM) + # 绑定IP地址和端口 + self.server_socket.bind(self.host_port) + # 监听 + self.server_socket.listen() + # 储存一个字典,储存客户端对话的会话线程 + self.server_thread_dict = {} + + """—————————按钮绑定操作—————————""" + self.Bind(wx.EVT_BUTTON, self.start_server, conn_start) + self.Bind(wx.EVT_BUTTON, self.save_data, conn_save) + self.Bind(wx.EVT_BUTTON, self.stop_server, conn_stop) + + def start_server(self, event): + # 首先判断服务器是否已近启动 + if not self.state: + # 创建主线程 + main_thread = Thread(target=self.server_main) + # 设置为守护线程,父线程(窗体)执行结束后子线程也自动关闭 + main_thread.daemon = True + # 修改服务器状态 + self.state = True + # 启动主线程 + main_thread.start() + + def server_main(self): + # 判断服务器启动状态 + while self.state: + # 输出服务器成功启动 + self.show_text.AppendText('-' * 40 + '\n服务器启动成功\n') + # 接收服务器的请求 + client_socket, client_addr = self.server_socket.accept() + # 客户端发送请求后第一个请求,发送过来的第一条数据为客户端的名称,客户端的名称作为字典的键 + user_name = client_socket.recv(1024).decode('utf-8') + # 创建一个会话线程 + session_thread = SessionThread(client_socket, user_name, self) + # 储存到字典 + self.server_thread_dict[user_name] = session_thread + # 启动会话线程 + session_thread.start() + # 输出服务器的提示信息 + self.show_info_and_send_client('服务器通知', f"欢迎{user_name}进入聊天室", + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + + self.server_socket.close() + + def show_info_and_send_client(self, data_source, data, datatime): + if self.state: + show_info = f"{data_source}: {data}\n时间:{datatime}" + self.show_text.AppendText('-' * 40 + '\n' + show_info + '\n') + for client in self.server_thread_dict.values(): + if client.state: + client.client_socket.send(show_info.encode('utf-8')) + + def save_data(self, event): + time_now = time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime()) + if not os.path.exists('chat data'): + os.mkdir('chat data') + with open(rf"chat data\{time_now}.txt", 'w+') as f: + data = self.show_text.GetValue() + f.write(data) + self.show_text.AppendText(rf"文件成功保存在\chat data\{time_now}.txt") + + def stop_server(self, event): + if self.state: + self.show_info_and_send_client('服务器通知', "服务器关闭", + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + self.show_text.AppendText('服务器通知 服务器关闭' + + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + self.state = False + + +class SessionThread(threading.Thread): + def __init__(self, client_socket, user_name, server): + # 调用父类的初始方法 + threading.Thread.__init__(self) + self.client_socket = client_socket + self.user_name = user_name + self.server = server + self.state = True # 会话线程是否启动,当创建线程自动启动 + + def run(self) -> None: + while self.state: + # 从客户端接收数据,存储在data中 + data = self.client_socket.recv(1024).decode('utf-8') + # 如果客户端点击断开连接,先给服务器发送一句话,消息自定义 + if data == 'client-exit': + self.server.show_info_and_send_client('服务器通知', f"{self.user_name}退出了聊天室", + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + self.state = False + else: + print(data) + print(self.user_name) + self.server.show_info_and_send_client(self.user_name, data, + time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) + + # 关闭socket连接 + self.client_socket.close() + + +if __name__ == '__main__': + # 初始化app() + app = wx.App() + # 创建直接的客户端对象 + frame = Myserver() + frame.Show() + + # 循环刷新显示 + app.MainLoop()