前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python实现局域网内屏幕广播的技术要点分析

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

作者头像
Python小屋屋主
发布2018-04-16 15:11:49
1.4K0
发布2018-04-16 15:11:49
举报
文章被收录于专栏:Python小屋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()

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-06-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python小屋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档