《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()
因为收发共用一个显示器,所以,真执行起来,可能不是很好看,但绝对是同时收发了。如果真做成程序,需要收消息在一个输入框里,发消息在一个显示框里。实际执行效果如下:
本文地址:https://blog.csdn.net/inside802/article/details/110941056