Python实现局域网内屏幕广播的技术要点分析

为更好地保证教学质量和提高学生的学习积极性,我使用Python开发了一套课堂教学管理系统,具有在线点名、在线答疑、随机提问、在线作业管理、在线自测、在线考试、数据汇总、试卷生成、屏幕广播等功能,教师端运行界面如下图所示:

学生端运行界面如下图所示:

该系统投入使用已有4个学期,效果非常好,不仅可以满足上课的各种需要,还可以作为“Python程序设计”课程的一个完整教学案例讲给学生,适用教材包括《Python程序设计基础》(董付国编著,清华大学出版社)、《Python程序设计(第2版)》(董付国编著,清华大学出版社)、《Python可以这样学》(董付国著,清华大学出版社)。本文重点介绍屏幕广播功能的技术要点,本系统界面使用tkinter编写,使用扩展库pillow实现屏幕截图,使用socket实现屏幕截图的传送,使用多线程技术实现多客户端的数据传输,文中略去了有关标准库和扩展库的导入代码。

1、学生端启动之后,监听UDP端口1000,等待教师端发送屏幕广播指令,代码如下:

def udpListen():

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 监听本机10000端口

sock.bind(('',10000))

while True:

data, addr = sock.recvfrom(100)

# 收到服务器发来的广播指令

if data == b'startBroadCast':

threading.Thread(target=receiveBroadCast).start()

sock.close()

threading.Thread(target=udpListen).start()

2、教师端通过界面上的按钮“开始屏幕广播”给局域网内所有学生端发送指令,同时监听TCP端口10001,等待学生端的连接,然后给每一个学生端连接发送本机屏幕截图,每0.5秒刷新一次。代码如下:

broadcasting = False

def broadcast(conn):

global broadcasting

while broadcasting:

time.sleep(0.8)

image = ImageGrab.grab()

size = image.size

imageBytes = image.tobytes()

length = len(imageBytes)

# 通知将要开始发送截图

conn.send(b'*****')

fhead = struct.pack('I32sI',

length,

str(size).encode(),

len(str(size).encode()))

conn.send(fhead)

conn.send(imageBytes)

else:

conn.send(b'#####')

conn.close()

def broadcastMain():

'''广播屏幕截图的主线程函数'''

global sockBroadCast

sockBroadCast = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

sockBroadCast.bind(('', 10001))

sockBroadCast.listen(150)

while broadcasting:

try:

conn, addr = sockBroadCast.accept()

except:

return

threading.Thread(target=broadcast, args=(conn,)).start()

else:

sockBroadCast.close()

def onbuttonStartBroadCastClick():

global broadcasting

broadcasting = True

# 启动服务器广播线程

threading.Thread(target=broadcastMain).start()

# 通知客户端开始接收广播

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

IP = socket.gethostbyname(socket.gethostname())

IP = IP[:IP.rindex('.')]+'.255'

sock.sendto(b'startBroadCast', (IP, 10000))

buttonStopBroadCast['state'] = 'normal'

buttonStartBroadCast['state'] = 'disabled'

buttonStartBroadCast = tkinter.Button(root, text='开始屏幕广播', command=onbuttonStartBroadCastClick)

buttonStartBroadCast.place(x=20, y=380, width=100, height=30)

def onbuttonStopBroadCastClick():

global broadcasting

broadcasting = False

sockBroadCast.close()

buttonStopBroadCast['state'] = 'disabled'

buttonStartBroadCast['state'] = 'normal'

buttonStopBroadCast = tkinter.Button(root, text='结束屏幕广播', command=onbuttonStopBroadCastClick)

buttonStopBroadCast['state'] = 'disabled'

buttonStopBroadCast.place(x=130, y=380, width=100, height=30)

3、学生端收到教师端通过UDP广播发送的屏幕广播指令之后,创建TCP Socket,连接教师端,并接收教师端发来的屏幕截图,然后使用创建顶端显示的tkinter界面用来显示屏幕截图。主要功能代码如下:

# 使用TCP接收广播

def receiveBroadCast():

# 获取屏幕尺寸,创建顶端显示的无标题栏窗体

screenWidth = 640

screenHeight = 480

top = tkinter.Toplevel(root,

width=screenWidth,

height=screenHeight)

top.overrideredirect(True)

# 顶端显示

top.attributes('-topmost', 1)

# 创建画布,用来显示图像

canvas = tkinter.Canvas(top,

bg='white',

width=screenWidth,

height=screenHeight)

canvas.pack(fill=tkinter.BOTH, expand=tkinter.YES)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

serverIP = entryServerIP.get()

# 连接服务器10001端口,失败直接返回

try:

sock.connect((serverIP, 10001))

except:

print('error')

top.destroy()

return

# 接收服务器指令

# *****表示开始传输一个新的截图

# #####表示本次广播结束

while True:

data = sock.recv(5)

if data == b'*****':

# 接收服务器发来的一屏图像

# 图像大小,字节总数量

len_head = struct.calcsize('I32sI')

data = sock.recv(len_head)

length, size, sizeLength = struct.unpack('I32sI', data)

length = int(length)

size = eval(size[:int(sizeLength)])

rest = length

image = []

while True:

if rest == 0:

break

elif rest > 40960:

temp = sock.recv(40960)

rest -= len(temp)

image.append(temp)

else:

temp = sock.recv(rest)

rest -= len(temp)

image.append(temp)

image = b''.join(image)

# 更新显示

image = Image.frombytes('RGB', size, image)

image = image.resize((screenWidth, screenHeight))

image = ImageTk.PhotoImage(image)

try:

canvas.delete(imageId)

except:

pass

imageId = canvas.create_image(screenWidth//2, screenHeight//2, image=image)

elif data == b'#####':

# 广播结束

break

# 本次广播结束,关闭窗口

sock.close()

top.destroy()

原文发布于微信公众号 - Python小屋(Python_xiaowu)

原文发表时间:2017-06-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏软件开发 -- 分享 互助 成长

Altium designer总结

大概有半年没有画过PCB板了,最近突然又要画一个简单的小板子,却发现好多东西已经不是很熟练了,现在把Altium designer软件的使用中要注意的问题和一些...

24610
来自专栏java一日一条

高效Android开发者必须知道的4个工具

移动app开发是一个漫长而费力的过程。然而,现在的企业总是希望能够尽快发布app。幸运的是,我们有很多帮助移动开发人员加快工作步伐的工具。

1371
来自专栏Coding01

推荐一款快速制作通讯录的微信小插件

只要有通讯录的地方,就会需要对通讯录姓名进行分组排序和界面设计,主流做法基本是按照人名的拼音首字母排序,效果图如下:

2362
来自专栏黄Java的地盘

基于React与Redux的留言墙的实现

由于某事业群需要留言墙用于年会,同时需要调用大象公众号服务器接口,所以在今年年初开发了留言墙用于活动现场交流。

2241
来自专栏進无尽的文章

实践-做一个会性能调优的好猿

对于每位 iOS 开发者来说,代码性能是个避不开的话题。随着项目的扩大和功能的增多,没经过认真调试和优化的代码,要么任性地卡顿运行,要么低调地崩溃。一般性能测试...

812
来自专栏疯狂的小程序

实例分享微信小程序项目搭建(上)

有幸能够参与我司【更美小程序】的搭建,在此分享些心得希望能够帮助到前端界萌新。因【更美小程序】源码需保密,我仅向大家分享基础建设级别的非业务代码。

7889
来自专栏魏艾斯博客www.vpsss.net

屏幕 GIF 动态图捕获软件及录制过程

作为站长有时候需要录制屏幕 GIF 动态图片,就得现学现卖。这不魏艾斯博客在使用遨游浏览器的过程中遇到一些 bug 需要提交给官方,遨游技术员希望老魏提供一段正...

2392
来自专栏图像识别与深度学习

2018-04-30项目遇到的问题及处理方法

1874
来自专栏企鹅号快讯

再见!陪伴我多年的setuna

hi,又到了charming的时间,平日里用setuna截图小工具的人肯定大有人在,但是最近我发现了一个比它更好用的小工具——PureRef,借此机会推荐一下~...

29310
来自专栏前端小记

用字蛛动态遍历JS生成中文字体

字蛛 font-spider 通过分析本地 CSS 与 HTML 文件获取 WebFont 中没有使用的字符,并将这些字符数据从字体中删除以实现压缩,同时生成跨...

69927

扫码关注云+社区

领取腾讯云代金券