使用Python简单实现客户端界面

服务端实现

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()
相关推荐
精灵vector8 分钟前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习23 分钟前
Python入门Day2
开发语言·python
Vertira26 分钟前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉30 分钟前
Python之 sorted() 函数的基本语法
python
项目題供诗1 小时前
黑马python(二十四)
开发语言·python
晓13131 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~1 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain
AIGC包拥它1 小时前
提示技术系列——链式提示
人工智能·python·langchain·prompt
孟陬1 小时前
Python matplotlib 如何**同时**展示正文和 emoji
python
何双新2 小时前
第 1 课:Flask 简介与环境配置(Markdown 教案)
后端·python·flask