import wx
from socket import *
import threading
import time
class Server(wx.Frame):
"""
服务器窗口类,继承自wx.Frame,用于创建服务器的图形用户界面和处理网络通信。
"""
def __init__(self):
"""
初始化服务器窗口,设置界面组件和网络通信相关属性。
"""
# 初始化窗口
wx.Frame.__init__(self, None, 1, '创想未来', size=(600, 400), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
self.Interface = None
self.init_components() # 初始化组件
self.Center() # 窗口居中
# 初始化服务器运行状态
self.isON = False
# 服务器地址和端口设置
self.host_port = ('', 8899)
# 初始化服务器套接字并绑定地址和端口
self.server_socket = socket(AF_INET, SOCK_STREAM)
self.server_socket.bind(self.host_port)
# 设置最大连接数
self.server_socket.listen(1000)
# 初始化用于存储客户端会话线程的映射
self.session_thread_map = {}
def init_components(self):
"""
初始化窗口组件,包括创建面板、按钮和文本控件。
"""
# 创建一个面板
# 创建一个面板
panel = wx.Panel(self)
# 初始化一个垂直方向的布局管理器
box = wx.BoxSizer(wx.VERTICAL)
# 将布局管理器设置到面板上
panel.SetSizer(box)
# 创建一个灵活的网格布局管理器,用于水平布局
gl = wx.FlexGridSizer(wx.HORIZONTAL)
# 创建“启动服务”按钮
start_up = wx.Button(panel, size=(100, 50), label='启动服务')
# 创建“保存记录”按钮
export = wx.Button(panel, size=(100, 50), label='保存记录')
# 将按钮添加到网格布局管理器中
gl.Add(start_up, 1, wx.TOP)
gl.Add(export, 1, wx.TOP)
# 将网格布局管理器添加到垂直布局管理器中,并居中
box.Add(gl, 1, wx.ALIGN_CENTER)
# 创建一个用于显示信息的文本控件
self.Interface = wx.TextCtrl(panel, size=(600, 300), style=wx.TE_MULTILINE | wx.TE_READONLY)
# 将文本控件添加到布局管理器中,并居中
box.Add(self.Interface, 2, wx.ALIGN_CENTER)
# 绑定按钮事件:启动服务按钮绑定到start_server方法,保存记录按钮绑定到save_record方法
start_up.Bind(wx.EVT_BUTTON, self.start_server)
export.Bind(wx.EVT_BUTTON, self.save_record)
def start_server(self, event):
"""
启动服务器按钮的事件处理函数,启动服务器并开始监听客户端连接。
"""
self.Interface.AppendText('服务启动成功。\n') # 向接口中追加文本,表示服务已经成功启动。
# 下面的代码块用于检查服务是否处于开启状态,如果之前是关闭的(即isON为False),则启动服务。
if not self.isON:
self.isON = True # 将isON设置为True,表示服务现在处于开启状态。
main_thread = threading.Thread(target=self.do_work) # 创建一个新的线程,目标函数为do_work。
main_thread.start() # 启动线程,开始执行do_work函数。
def save_record(self, event):
"""
保存记录按钮的事件处理函数,将聊天记录保存到文件中。
参数:
- self: 表示实例自身
- event: 触发函数的事件对象
返回值:
- 无
"""
# 获取聊天记录
record = self.Interface.GetValue()
# 打开文件,准备写入聊天记录
with open('record.txt', 'w+', encoding='utf-8') as f:
f.write(record) # 将记录写入文件
# 在界面中添加文本,提示记录已成功保存
self.Interface.AppendText('保存记录成功。\n')
def do_work(self):
"""
服务器工作线程函数,用于处理客户端连接和消息收发。
参数:
self: 对象自身的引用。
返回值:
无
"""
# 启动时的提示信息
self.Interface.AppendText('聊天室启动正常,等待客户端加入.....\n')
while self.isON:
# 接受客户端连接
session_socket, client_addr = self.server_socket.accept()
# 接收客户端发送的用户名
user_name = session_socket.recv(1024).decode('utf-8')
# 创建一个新的会话线程并启动
session_thread = SessionThread(session_socket, user_name, self)
self.session_thread_map[user_name] = session_thread
session_thread.start()
# 显示客户端加入的信息
self.show_info_and_client('系统:', '欢迎"%s"加入聊天室' % user_name, time.strftime('%Y-%m-%d %H:%M:%S'))
# 关闭服务器端套接字
self.server_socket.close()
def show_info_and_client(self, source, data, data_time):
"""
向所有客户端和服务器文本框发送消息的函数。
:param source: 消息来源,表明消息是从哪个源发送的。
:param data: 消息内容,发送的实际消息文本。
:param data_time: 消息时间戳,标记消息发送的时间。
"""
# 格式化消息,包括来源、内容和时间戳
send_data = '%s:%s\n时间:%s\n' % (source, data, data_time)
# 在服务器界面文本框中添加消息
self.Interface.AppendText('\n%s' % send_data)
# 遍历所有客户端,向在线客户端发送消息
for client in self.session_thread_map.values():
if client.isON:
# 编码消息并发送给客户端
client.user_socket.send(send_data.encode('utf-8'))
class SessionThread(threading.Thread):
"""
客户端会话线程类,继承自threading.Thread,用于处理单个客户端的通信。
"""
def __init__(self, user_socket, user_name, server):
"""
初始化客户端会话线程。
:param user_socket: 客户端的socket连接
:param user_name: 客户端用户名
:param server: 服务器实例
"""
# 调用父类的构造函数,初始化线程
threading.Thread.__init__(self)
# 存储客户端的socket连接
self.user_socket = user_socket
# 存储客户端的用户名
self.user_name = user_name
# 存储服务器实例的引用
self.server = server
# 客户端会话线程的运行状态,默认为True表示线程正在运行
self.isON = True
def run(self):
"""
客户端会话线程的运行函数,用于处理客户端的消息收发。
"""
while self.isON:
# 在循环中不断接收客户端发送的消息
data = self.user_socket.recv(2048).decode("utf-8")
if data == 'bye':
self.isON = False
# 当接收到"bye"消息时,更新状态并发送离开通知
self.server.show_info_and_client("系统:", "%s离开" % self.user_name,
time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 向所有客户端和服务器文本框显示消息
self.server.show_info_and_client(self.user_name, data, time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 在退出循环后关闭客户端的socket连接
self.user_socket.close()
# 如果当前脚本作为主程序运行,则创建一个wxPython应用程序实例
if __name__ == '__main__':
# 初始化wxPython应用程序
app = wx.App()
# 显示Server窗口
Server().Show()
# 进入应用程序主事件循环
app.MainLoop()
运行后示例:

评论前必须登录!
注册