双工模式,意味着收消息不耽误发消息,发消息不耽误收消息,收发双工,说的直白一点,这就是让我写一个精简版的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()

因为收发共用一个显示器,所以,真执行起来,可能不是很好看,但绝对是同时收发了。如果真做成程序,需要收消息在一个输入框里,发消息在一个显示框里。实际执行效果如下:

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