Python Notes 04 (Multi-threading-Callback Function-Chat Room)

a callback function

Callback function is a common concept in programming. It is usually used in asynchronous programming or event-driven programming. A callback function is a function that is passed as a parameter to another function and is called when a specific event occurs.

Here are some basic concepts and usage of callback functions in Python:

  1. Functions as objects : In Python, functions are first-class citizens, which means they can be passed around like variables. You can pass a function as a parameter to another function, or assign a function to a variable.

  2. Definition of callback function: A callback function is usually an ordinary Python function that can be called when needed. This function accepts one or more parameters, usually the data when the event is triggered.

  3. Registration of callback function: In some cases, you need to register the callback function to an event handler or asynchronous task. This is usually done by passing a callback function as a parameter to another function or method of the object.

  4. Event triggering: When a specific event occurs, the registered callback function will be called. This event can be the completion of an asynchronous operation, user interaction, timer triggering, etc.

Here is a simple example demonstrating how to use callback functions in Python:

# 定义一个回调函数
def callback_function(result):
    print(f"Callback received result: {result}")

# 模拟异步操作,当操作完成时调用回调函数
def async_operation(callback):
    # 模拟异步操作,假设操作需要一段时间
    import time
    time.sleep(2)
    result = 42  # 模拟操作的结果
    # 调用回调函数,将结果传递给回调函数
    callback(result)

# 注册回调函数并执行异步操作
async_operation(callback_function)

# 主程序可以继续执行其他操作
print("Main program continues...")

1.1 Process pool callback function

pool.apply_async (func=test1 , callback=test3) # Call the callback function

import time, os
from multiprocessing import Pool


def test1():
    print(f'妈妈的进程ID:{os.getpid()}, 当前进程的父进程:{os.getppid()}')
    print(f'你女儿叫你起床,你慢吞吞的起床')
    time.sleep(3)
    print('你起来了')
    return 'good morning'


def test2():
    print(f'女儿进程,女儿开始早读,当前进程ID:{os.getpid()}')
    time.sleep(3)
    print('早读完成')


def test3(args):
    print('一起吃早餐')
    print(args)


if __name__ == '__main__':
    pool = Pool(4)
    pool.apply_async(func=test1, callback=test3)
    test2()

Two-person chat room server code

import wx
from socket import *
import threading
import time


class ChatServer(wx.Frame):
    def __init__(self):
        """创建窗口"""
        wx.Frame.__init__(self, None, id=102, title='服务器界面', pos=wx.DefaultPosition, size=(400, 470))
        pl = wx.Panel(self)  # 在窗口中初始化一个面板
        # 在面板里面会放一些按钮,文本框,文本输入框等,把这些对象统一放入一个盒子里面
        box = wx.BoxSizer(wx.VERTICAL)  # 在盒子里面垂直方向自动排版

        g1 = wx.FlexGridSizer(wx.HORIZONTAL)  # 可升缩的网格布局,水平方向
        # 创建三个按钮
        start_server_button = wx.Button(pl, size=(133, 40), label="启动")
        record_save_button = wx.Button(pl, size=(133, 40), label="聊天记录保存")
        stop_server_button = wx.Button(pl, size=(133, 40), label="停止")
        g1.Add(start_server_button, 1, wx.TOP)
        g1.Add(record_save_button, 1, wx.TOP)
        g1.Add(stop_server_button, 1, wx.TOP)
        box.Add(g1, 1, wx.ALIGN_CENTER)  # ALIGN_CENTER 联合居中

        # 创建只读的文本框,显示聊天记录
        self.text = wx.TextCtrl(pl, size=(400, 400), style=wx.TE_MULTILINE | wx.TE_READONLY)
        box.Add(self.text, 1, wx.ALIGN_CENTER)
        pl.SetSizer(box)
        '''以上代码窗口结束 '''

        '''服务准备执行的一些属性'''
        self.isOn = False  # 服务器没有启动
        self.host_port = ('', 8888)
        self.server_socket = socket(AF_INET, SOCK_STREAM)  # TCP协议的服务器端套接字
        self.server_socket.bind(self.host_port)
        self.server_socket.listen(5)
        self.session_thread_map = {}  # 存放所有的服务器会话线程,字典:客户端名字为Key,会话线程为Value

        '''给所有的按钮绑定相应的动作'''  # 给启动按钮,绑定一个按钮事件,事件触发的时候会自动调用一个函数
        self.Bind(wx.EVT_BUTTON, self.start_server, start_server_button)
        self.Bind(wx.EVT_BUTTON, self.save_record, record_save_button)

    # 服务器开始启动函数
    def start_server(self, event):
        print('服务器开始启动')
        if not self.isOn:
            # 启动服务器
            self.isOn = True
            # 创建线程对象
            main_thread = threading.Thread(target=self.do_work)
            main_thread.setDaemon(True)  # 设置守护线程
            main_thread.start()

    # 服务运行之后的函数
    def do_work(self):
        print("服务器开始工作")
        while self.isOn:  # 在start_server中被赋值为True
            session_socket, client_addr = self.server_socket.accept()
            # 服务首先接受客户端发过来的第一条消息,我们规定第一条消息为客户端的名字
            username = session_socket.recv(1024).decode('UTF-8')  # 接受客户端名字

            # 创建一个会话线程 【采用的是继承式实现多线程】
            session_thread = SessionThread(session_socket, username, self)
            # 字典元素的添加 {客户名称key:客户端的线程对象为值}
            self.session_thread_map[username] = session_thread
            session_thread.start()  # 启动客户端的线程
            # 表示有客户端进入到聊天室
            self.show_info_and_send_client("服务器通知", f"欢迎{username}进入聊天室!",
                                           time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
        self.server_socket.close()

    # 在文本中显示接受和发送的信息
    def show_info_and_send_client(self, source, data, data_time):
        send_data = f'{source}:{data}\n 时间:{data_time}\n'

        self.text.AppendText(f'--------------------\n{send_data}')  # 在服务器聊天框显示
        for client in self.session_thread_map.values():
            if client.isOn:
                client.user_socket.send(send_data.encode('utf-8'))   # 数据转发到所有没有关闭的客户端

    # 服务保存聊天记录
    def save_record(self, event):
        record = self.text.GetValue()
        with open("record.log", "w+") as f:
            f.write(record)


# 服务器端会话线程的类
class SessionThread(threading.Thread):
    def __init__(self, socket, un, server):
        threading.Thread.__init__(self)
        self.user_socket = socket
        self.username = un
        self.server = server
        self.isOn = True   # 会话是否已经启动

    def run(self):
        print(f'客户端{self.username}, 已经和服务器连接成功,服务器启动一个会话线程')
        while self.isOn:
            data = self.user_socket.recv(1024).decode('utf8')  # 接收聊天信息
            if data == 'A^disconnect^B':   # 客户端点击断开按钮,约定内容
                self.isOn = False
                self.server.show_info_and_send_client(f'服务器通知,{self.username}离开了聊天室!'
                                                      f'{time.strftime("%Y-%m-%d %H:%M:%S")}')
            else:
                # 其他聊天信息
                self.server.show_info_and_send_client(self.username, data, time.strftime("%Y-%m-%d %H:%M:%S"))
        self.user_socket.close()


if __name__ == '__main__':
    app = wx.App()
    ChatServer.Show()
    app.MainLoop()

Three-person chat room client code

import wx
from socket import *
import threading


class ChatClient(wx.Frame):
    def __init__(self, c_name):  # c_name:客户端名字
        # 调用父类的初始化函数
        wx.Frame.__init__(self, None, id=101, title=f'{c_name}的客户端界面', pos=wx.DefaultPosition, size=(400, 470))
        pl = wx.Panel(self)  # 在窗口中初始化一个面板
        # 在面板里面会放一些按钮,文本框,文本输入框等,把这些对象统一放入一个盒子里面
        box = wx.BoxSizer(wx.VERTICAL)  # 在盒子里面垂直方向自动排版

        g1 = wx.FlexGridSizer(wx.HORIZONTAL)  # 可升缩的网格布局,水平方向
        # 创建两个按钮
        conn_button = wx.Button(pl, size=(200, 40), label="连接")
        dis_conn_button = wx.Button(pl, size=(200, 40), label="离开")
        g1.Add(conn_button, 1, wx.TOP | wx.LEFT)  # 连接按钮布局在左边
        g1.Add(dis_conn_button, 1, wx.TOP | wx.RIGHT)  # 断开按钮布局在右边
        box.Add(g1, 1, wx.ALIGN_CENTER)  # ALIGN_CENTER 联合居中

        # 创建聊天内容的文本框,不能写消息 :TE_MULTILINE -->多行  TE_READONLY-->只读
        self.text = wx.TextCtrl(pl, size=(400, 250), style=wx.TE_MULTILINE | wx.TE_READONLY)
        box.Add(self.text, 1, wx.ALIGN_CENTER)

        # 创建聊天的输入文本框,可以写
        self.input_text = wx.TextCtrl(pl, size=(400, 100), style=wx.TE_MULTILINE)
        box.Add(self.input_text, 1, wx.ALIGN_CENTER)

        # 最后创建两个按钮,分别是发送和重置
        g2 = wx.FlexGridSizer(wx.HORIZONTAL)
        clear_button = wx.Button(pl, size=(200, 40), label="重置")
        send_button = wx.Button(pl, size=(200, 40), label="发送")
        g2.Add(clear_button, 1, wx.TOP | wx.LEFT)
        g2.Add(send_button, 1, wx.TOP | wx.RIGHT)
        box.Add(g2, 1, wx.ALIGN_CENTER)

        pl.SetSizer(box)  # 把盒子放入面板中

        ''' 以上代码完成了客户端界面(窗口) '''

        '''给所有按钮绑定点击事件'''
        self.Bind(wx.EVT_BUTTON, self.connect_to_server, conn_button)
        self.Bind(wx.EVT_BUTTON, self.send_to, send_button)
        self.Bind(wx.EVT_BUTTON, self.go_out, dis_conn_button)
        self.Bind(wx.EVT_BUTTON, self.reset, clear_button)

        '''客户端的属性'''
        self.name = c_name
        self.isConnected = False  # 客户端是否已经连上服务器
        self.client_socket = None

    # 连接服务器
    def connect_to_server(self, event):
        print(f"客户端{self.name},开始连接服务器")
        if not self.isConnected:
            server_host_port = ('localhost', 8888)
            self.client_socket = socket(AF_INET, SOCK_STREAM)
            self.client_socket.connect(server_host_port)
            # 之前规定,客户端只要连接成功,马上把自己的名字发给服务器
            self.client_socket.send(self.name.encode('UTF-8'))
            t = threading.Thread(target=self.receive_data)  # 客户端,采用函数式实现多线程
            t.setDaemon(True)  # 客户端UI界面如果关闭,当前守护线程也自动关闭
            self.isConnected = True
            t.start()

    # 接受聊天数据
    def receive_data(self):
        print("客户端准备接收服务器的数据")
        while self.isConnected:
            data = self.client_socket.recv(1024).decode('UTF-8')
            # 从服务器接收到的数据,需要显示
            self.text.AppendText(f'{data}\n')

    # 客户端发送信息到聊天室
    def send_to(self, event):
        if self.isConnected:
            info = self.input_text.GetValue()
            if info != '':
                self.client_socket.send(info.encode('UTF-8'))
                # 输入框中的数据如果已经发送了,输入框重新为空
                self.input_text.SetValue('')

    # 客户端离开聊天
    def go_out(self, event):
        self.client_socket.send('A^disconnect^B'.encode('UTF-8'))
        # 客户端主线程也关闭
        self.isConnected = False

    # 客户端的输入信息重置
    def reset(self, event):
        self.input_text.Clear()


if __name__ == '__main__':
    app = wx.App()
    name = input("请输入客户端名字:")
    ChatClient(name).Show()
    app.MainLoop()  # 循环刷新显示

Guess you like

Origin blog.csdn.net/March_A/article/details/133325480