一.接着上一篇文章,实现了客户端和服务器端通讯,那么,客户端与客户端之间可以相互发送消息吗,大学的时候记得有个c语言项目是这样实现的:客户端和服务器端之间用tcp连接并记录客户端ip和端口号,客户端和客户端用udp连接发送消息(客户端ip和端口号从服务器端获取)。
今天试下利用队列实现客户端和客户端发送消息。
原理:多个客户端同时在线,服务器端会产生多个线程,那么这些线程之间可以利用队列Queue实现相互发送消息。
二. 先看下队列怎么使用,如下代码:queue_test.py
from queue import Queue
from threading import Thread
import time
class Student(Thread):
def __init__(self, name, queue):
super().__init__()
self.name = name
self.queue = queue
def run(self):
while True:
# 阻塞程序,时刻监听老师,接收消息
msg = self.queue.get()
# 一旦发现点到自己名字,就赶紧答到
if msg == self.name:
print("{}:到!".format(self.name))
else:
pass
#self.queue.put(msg)
class Teacher:
def __init__(self, queue):
self.queue=queue
def call(self, student_name):
print("老师:{}来了没?".format(student_name))
# 发送消息,要点谁的名
self.queue.put(student_name)
if __name__ == "__main__":
queue = Queue()
teacher = Teacher(queue=queue)
s1 = Student(name="小明", queue=queue)
s2 = Student(name="小亮", queue=queue)
s1.start()
s2.start()
print('开始点名~')
teacher.call('小明')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
teacher.call('小明')
time.sleep(1)
运行结果如下:
代码中将两个线程加到队列中,
s1 = Student(name=“小明”, queue=queue)
s2 = Student(name=“小亮”, queue=queue)注意这个顺序对于后面的调用非常重要。
为了效果我们把 开始点名那个地方代码替换下面的代码:
print('开始点名~')
teacher.call('小亮')
time.sleep(1)
teacher.call('小明')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
运行结果如下:
结果中却没有出现学生喊到,为什么呢,没错,因为队列,小明的线程排在第一个,所有不管老师喊谁,小明第一个取数据发现不是自己不打印到,接着小亮,再小明…这样就会出现上面老师点名没有一个学生到。我们可以做个小修改,在小明一个取数据发现不是自己的时候,将数据重新放到队列中,让其他人取。
student类中添加else如下:
# 一旦发现点到自己名字,就赶紧答到
if msg == self.name:
print("{}:到!".format(self.name))
else:
self.queue.put(msg)
结果如下:
思考,如果点名不在这个队列中,如小花,会怎样呢。当然会导致代码出现死循环
修改queue_test.py代码如下:
from queue import Queue
from threading import Thread
import time
all_student = list()
class Student(Thread):
global all_student
def __init__(self, name, queue):
super().__init__()
self.name = name
self.queue = queue
all_student.append(name)
def run(self):
while True:
# 阻塞程序,时刻监听老师,接收消息
msg = self.queue.get()
# 一旦发现点到自己名字,就赶紧答到
if msg == self.name:
print("{}:到!".format(self.name))
else:
if all_student.__contains__(msg):
print(msg)
self.queue.put(msg)
time.sleep(0.001)
class Teacher:
def __init__(self, queue):
self.queue=queue
def call(self, student_name):
print("老师:{}来了没?".format(student_name))
# 发送消息,要点谁的名
self.queue.put(student_name)
def main():
queue = Queue()
teacher = Teacher(queue=queue)
s1 = Student(name="小明", queue=queue)
s2 = Student(name="小亮", queue=queue)
s1.start()
s2.start()
print('开始点名~')
teacher.call('小话')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
teacher.call('小明')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
s3 = Student(name="小话", queue=queue)
s3.start()
teacher.call('小话')
time.sleep(1)
teacher.call('小亮')
time.sleep(1)
teacher.call('小明')
if __name__ == "__main__":
main()
添加全局变量all_student = list(),存放student线程,如果点名不在student线程中就不put数据到队列中。
总结:队列先进先出,先取数据。如上问题,点名小花,会导致代码死循环,可以设置标志,当重新遇到自己线程设置的标志位就暂停put数据到队列中。下一章基于这个原理实现socket客户端之间相互发送消息