python
复制代码
import threading
import time
import wx
from socket import socket, AF_INET, SOCK_STREAM
class LServer(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, id=1002, title='L服务器端界面', pos=wx.DefaultPosition, size=(400, 450))
# 窗口中添加面板
pl = wx.Panel(self)
# 创建一个盒子
box = wx.BoxSizer(wx.VERTICAL)
# 创建可伸缩的网格布局
fgz1 = wx.FlexGridSizer(wx.HSCROLL) # 水平方向布局
# 创建按钮
start_server_btn = wx.Button(pl, size=(133, 40), label='启动服务')
record_btn = wx.Button(pl, size=(133, 40), label='保存记录')
stop_server_btn = wx.Button(pl, size=(133, 40), label='停止服务')
# 将按钮添加到网络布局中
fgz1.Add(start_server_btn, 1, wx.TOP | wx.LEFT)
fgz1.Add(record_btn, 1, wx.TOP | wx.CENTRE)
fgz1.Add(stop_server_btn, 1, wx.TOP | wx.RIGHT)
# 将可伸缩的网格布局添加到box中
box.Add(fgz1, 1, wx.ALIGN_CENTRE)
# 只读文本框,用于显示聊天内容
self.show_text = wx.TextCtrl(pl, size=(400, 410), style=wx.TE_MULTILINE | wx.TE_READONLY)
box.Add(self.show_text, 1, wx.ALIGN_CENTRE)
# 把盒子放到面板中
pl.SetSizer(box)
'''-----------------------------以上代码为界面的绘制代码-----------------------------------'''
'''------------------------------设置服务器功能实现的必要属性----------------------------------'''
self.isOn = False # 存储服务器的启动状态,默认False没有启动
# 服务器端绑定的IP地址和端口
self.host_port = ('', 8888) # 空的字符串代表的是本机的所有IP
# 创建Socket对象
self.server_socket = socket(AF_INET, SOCK_STREAM)
# 绑定IP地址和端口
self.server_socket.bind(self.host_port)
# 监听
self.server_socket.listen(5)
# 创建一个字典,存储与客户端对话的会话线程
self.session_thread_dict = {} # key-value(客户端的名称key:绘画线程value)
'''----------------------------------------------------------------'''
# 当鼠标点击'启动服务'按钮时,需要执行的操作
self.Bind(wx.EVT_BUTTON, self.start_server, start_server_btn)
self.Bind(wx.EVT_BUTTON, self.save_record, record_btn)
self.Bind(wx.EVT_BUTTON, self.stop_server, stop_server_btn)
def stop_server(self,event):
#
print('服务器已停止服务')
self.isOn=False
def save_record(self,event):
#获取只读文本框的内容
record_data=self.show_text.GetValue()
with open('record.log','w',encoding='utf-8') as file:
file.write(record_data)
def start_server(self, event):
# 判断服务器是否已经启动,只有服务器没有启动时菜启动
if not self.isOn: # 等监狱self.isOn==False
# 启动服务
self.isOn = True
# 创建主线程对象,函数式创建主线程
main_thread = threading.Thread(target=self.do_work)
# 设置守护线程,父线程执行结束(窗口界面)子线程也自动关闭
main_thread.daemon = True
# 启动主线程
main_thread.start()
def do_work(self):
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_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.isOn的值为False时,执行关闭Socket对象
self.server_socket.close()
def show_info_and_send_client(self, data_source, data, data_time):
# 字符串拼接操作
send_data = f'{data_source}:{data}\n时间:{data_time}'
# 只读文本框
self.show_text.AppendText('-' * 40 + '\n' + send_data + '\n')
# 每一个客户端都发送一次
for client in self.session_thread_dict.values():
# 判断当前的会话是否为开启状态
if client.isOn:
client.client_socket.send(send_data.encode('utf-8'))
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.isOn = True # 会话线程是否启动,当创建SessionThread对象是,会话线程就启动了,所以当前默认为True
#
def run(self):
print(f'客户端:{self.user_name}已经与服务端建立连接.')
while self.isOn:
# 从客户端接收数据
data = self.client_socket.recv(1024).decode('utf-8');
# 如果客户端点击断开按钮,先给服务器发送断开连接,消息自定义 C-DISCONNECT-S 自定义结束词
if data == 'C-DISCONNECT-S':
self.isOn = False
# 发送一条服务器通知
# 其他聊天信息显示给所有客户端,包含服务器显示
self.server.show_info_and_send_client('服务器通知', f'{self.user_name}离开了聊天室',
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
else:
# 其他聊天信息显示给所有客户端,包含服务器显示
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()
# 创建自己的客户端界面对象
client = LServer()
client.Show() # 可以改成LServer().Show()
# 循环刷新显示
app.MainLoop()
python
复制代码
# coding:utf-8
import threading
import wx
from socket import socket, AF_INET, SOCK_STREAM
class LClinet(wx.Frame):
def __init__(self, clent_name):
# 调用父类的初始化方法
# None:没有父级窗口
# id:表示当前窗口的一个编号
# title:窗口标题
# pos:窗体的打开位置
# size:窗体的大小;单位是像素,400宽,450高
wx.Frame.__init__(self, None, id=1001, title=clent_name + '的客户端界面', pos=wx.DefaultPosition,
size=(400, 600))
# 创建面板对象
pl = wx.Panel(self)
# 在面板中放上盒子
box = wx.BoxSizer(wx.VERTICAL) # 垂直方向布局
# 可伸缩的网格布局
fgz1 = wx.FlexGridSizer(wx.HSCROLL) # 水平方向布局
# 创建连个按钮
connect_btn = wx.Button(pl, size=(200, 40), label='连接')
disconnect_btn = wx.Button(pl, size=(200, 40), label='断开')
# 把两个按钮放到可伸缩的网格布局
fgz1.Add(connect_btn, 1, wx.TOP | wx.LEFT)
fgz1.Add(disconnect_btn, 1, wx.TOP | wx.RIGHT)
# 可伸缩的网格布局添加到box中
box.Add(fgz1, 1, wx.ALIGN_CENTRE)
# 只读文本框,用于显示聊天内容
self.show_text = wx.TextCtrl(pl, size=(400, 210), style=wx.TE_MULTILINE | wx.TE_READONLY)
box.Add(self.show_text, 1, wx.ALIGN_CENTRE)
# 创建聊天内容的文本框
self.chat_text = wx.TextCtrl(pl, size=(400, 210), style=wx.TE_MULTILINE)
box.Add(self.chat_text, 1, wx.ALIGN_CENTRE)
# 可伸缩的网格布局
fgz2 = wx.FlexGridSizer(wx.HSCROLL) # 水平方向布局
# 创建连个按钮
reset_btn = wx.Button(pl, size=(200, 40), label='重置')
send_btn = wx.Button(pl, size=(200, 40), label='发送')
# 把两个按钮放到可伸缩的网格布局
fgz2.Add(reset_btn, 1, wx.TOP | wx.LEFT)
fgz2.Add(send_btn, 1, wx.TOP | wx.LEFT)
# 可伸缩的网格布局添加到box中
box.Add(fgz2, 1, wx.ALIGN_CENTRE)
# 将盒子放到面板中
pl.SetSizer(box)
'''------------------以上代码时客户端界面的绘制---------------------'''
self.Bind(wx.EVT_BUTTON, self.connect_to_server, connect_btn)
# 实例属性设置
self.client_name = clent_name
self.isConnected = False # 存储客户端连接服务器的状态,默认False未连接
self.client_socket = None # 设置客户端的socket对象为空
self.Bind(wx.EVT_BUTTON, self.send_to_server, send_btn)
self.Bind(wx.EVT_BUTTON, self.disconnect_to_server, disconnect_btn)
self.Bind(wx.EVT_BUTTON, self.reset, reset_btn)
def reset(self,event):
# 清空文本框
self.chat_text.SetValue('')
def disconnect_to_server(self,event):
#发送断开的信息
self.client_socket.send('C-DISCONNECT-S'.encode('utf-8'))
# 改变连接状态
self.isConnected=False
def send_to_server(self, event):
# 判断连接状态
if self.isConnected:
# 从可写文本框中获取输入的内容
input_data = self.chat_text.GetValue()
if input_data != '':
# 向服务器发送数据
self.client_socket.send(input_data.encode('utf-8'))
# 发送完数据后清空文本框
self.chat_text.SetValue('')
def connect_to_server(self, event):
print(f'客户端{self.client_name}连接服务器成功')
# 如果客户端没有连接服务器,则开始连接
if not self.isConnected: # 等价与self.isConnected==False
# TCP编程步骤
server_host_port = ('127.0.0.1', 8888)
# 创建socket对象
self.client_socket = socket(AF_INET, SOCK_STREAM)
# 发送连接请求
self.client_socket.connect(server_host_port)
# 只要连接成功,发送一条数据
self.client_socket.send(self.client_name.encode('utf-8'))
# 启动一个线程,客户端的线程与服务器的会话线程进行会话
client_thread = threading.Thread(target=self.recv_data)
# 设置线程守护,窗体关闭后,子线程也关闭
client_thread.daemon = True
# 修改连接状态
self.isConnected = True
# 启动线程
client_thread.start()
def recv_data(self):
# 判断是否是连接状态
while self.isConnected:
# 接收服务器的数据
data = self.client_socket.recv(1024).decode('utf-8')
# 显示到文本框中
self.show_text.AppendText('-' * 40 + '\n' + data + '\n')
if __name__ == '__main__':
# 初始化App()
app = wx.App()
name = input('请输入客户端名称:')
# 创建自己的客户端界面对象
client = LClinet(name)
client.Show() # 可以改成LClinet('LL').Show()
# 循环刷新显示
app.MainLoop()