欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

《Python核心编程》练习题之2-8:更新上一个练习的解决方案,修改它以使你的聊天服务现在成为全双工模式,意味着通信两端都可以发送并接收消息,并且二者相互独立

程序员文章站 2022-07-02 22:30:19
双工模式,意味着收消息不耽误发消息,发消息不耽误收消息,收发双工。这我想了想,那不得引入多线程吗?一个线程既做收,也做发,那必然要一个一个来,前面那个走不了,后面那个就得等,实现不了双工,因此我觉得必须使用多线程。而多线程是书上第四章讲的东西,却在第二章出这个题,不知道是为啥。服务器代码如下:from socket import *from threading import Threadclass MyThread(Thread): """Thread类的子类,socket收发消息都实例...

双工模式,意味着收消息不耽误发消息,发消息不耽误收消息,收发双工,说的直白一点,这就是让我写一个精简版的QQ程序啊。这我想了想,那不得引入多线程吗?一个线程既做收,也做发,那必然要一个一个来,前面那个走不了,后面那个就得等,实现不了双工,因此我觉得必须使用多线程。而多线程是书上第四章讲的东西,却在第二章出这个题,不知道是为啥。
服务器代码如下:

from socket import *
from threading import Thread


class MyThread(Thread):
    """Thread类的子类,socket收发消息都实例化这个类来进行多线程并发"""

    def __init__(self, func, args, name=''):
        """
        func参数是此线程要调用的函数名称
        args参数是此线程要调用的函数的参数
        name我也不知道是干啥用的,Thread类的初始化是有这个参数的,既然继承了Thread类,就带上这个参数
        """
        Thread.__init__(self)
        self.func = func
        self.args = args
        self.name = name

    def run(self):
        """线程启动时,将默认调用这个方法。这个函数的内容即执行func函数"""
        # 注意func内的参数前面带有*号。因为func函数可能不止一个参数,因此使用*号参数,传入的是一个元组。如果有2个及以上的参数,直接用()传递
        # 接上,如果只有1个参数,需要在参数后加逗号。比如func函数只有1个参数10,则在实例化MyThread类时,args的位置应写为(10,)
        self.func(*self.args)


def recv_message(work_socket, buffsize):
    """
    功能: 收消息的函数,此函数讲永远运行下去,暂时没有设计退出的方法
    work_socket参数是socket类型的,是socket通信建立成功后,进行通信的socket
    buffsize参数是work_socket每次收消息的缓存字节数
    """
    # 检查work_socket类型是否为socket类型。做此校验有2个好处,1个是能够使程序更健壮,
    # 另1个是方便下面在使用这个参数时pycharm能够自动关联socket类的相关方法
    if not isinstance(work_socket, socket):
        raise TypeError
    while True:
        recv_data = work_socket.recv(buffsize)
        print('接收到消息: ', recv_data.decode('utf-8'))


def send_message(work_socket):
    """
    功能:收消息的函数,此函数讲永远运行下去,暂时没有设计退出的方法
    work_socket参数是socket类型的,是socket通信建立成功后,进行通信的socket
    """
    # 检查work_socket类型是否为socket类型。做此校验有2个好处,1个是能够使程序更健壮,
    # 另1个是方便下面在使用这个参数时pycharm能够自动关联socket类的相关方法
    if not isinstance(work_socket, socket):
        raise TypeError
    while True:
        send_data = input('请输入要发送的消息:')
        work_socket.send(send_data.encode('utf-8'))


def main():
    """
    这个函数将创建并启动服务器socket,实现同时收发消息
    这里把启动服务器写到main函数里,而不是直接写,是因为我们在写客户端程序时会引用这个模块里的MyThread类、send_message、recv_message
    如果不写在函数里,当客户端模块引用这个服务器模块时,就会默认执行函数外的代码,这会带来异常
    """
    HOST = ''
    PORT = 18685
    BUFFSIZE = 1024
    ADDR = (HOST, PORT)

    # 创建服务器的socket并开始监听
    server_socket = socket(AF_INET, SOCK_STREAM)
    server_socket.bind(ADDR)
    server_socket.listen(1)

    print('服务器监听已打开,等待连接...')
    work_socket, addr = server_socket.accept()

    # 将服务器socket收消息的功能放入线程t1.此处func = recv_message, args = (work_socket, BUFFSIZE)
    t1 = MyThread(recv_message, (work_socket, BUFFSIZE))

    # 将服务器socket发消息的功能放入线程t2,此处func = send_message, args = (work_socket,)
    t2 = MyThread(send_message, (work_socket,))

    # 启动线程
    t1.start()
    t2.start()


if __name__ == '__main__':
    main()

客户端代码如下(注意我本地的服务器模块名称是tsTservTW.py):

from socket import *

# 引入服务器的模块,(我本地的服务器模块的名称叫tsTservTW.py)直接复用服务器模块的线程类和收发消息函数
from tsTservTW import MyThread, recv_message, send_message

HOST = '127.0.0.1'
PORT = 18685
BUFFSIZE = 1024
ADDR = (HOST, PORT)

client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(ADDR)

# 将客户端收消息的功能放入线程t1,此处func = recv_message, args = (client_socket, BUFFSIZE)
t1 = MyThread(recv_message, (client_socket, BUFFSIZE))

# 将客户端socket发消息的功能放入线程t2,此处func = send_message, args = (client_socket,)
t2 = MyThread(send_message, (client_socket,))

# 启动线程
t1.start()
t2.start()

因为收发共用一个显示器,所以,真执行起来,可能不是很好看,但绝对是同时收发了。如果真做成程序,需要收消息在一个输入框里,发消息在一个显示框里。实际执行效果如下:
《Python核心编程》练习题之2-8:更新上一个练习的解决方案,修改它以使你的聊天服务现在成为全双工模式,意味着通信两端都可以发送并接收消息,并且二者相互独立
《Python核心编程》练习题之2-8:更新上一个练习的解决方案,修改它以使你的聊天服务现在成为全双工模式,意味着通信两端都可以发送并接收消息,并且二者相互独立

本文地址:https://blog.csdn.net/inside802/article/details/110941056