• Python实现局域网内屏幕广播的技术要点分析(转载)


      转载:https://mp.weixin.qq.com/s?timestamp=1498531736&src=3&ver=1&signature=Eq6DPvkuGJi*G5spckebex6lYxUF**UjirOaOrRPQy8C8naLhUiAH-LIqNEmXvsjhalJZc3-uWbnntdmmer5raOoqYH4MAAQT9SoV5qrOJtdnNWJZAUxPlgzzCS1nv6ar3uoIIRG11OHhHPXVEU3pWcFP4Cxz5s7Kh1XjKqgOsg=

      为更好地保证教学质量和提高学生的学习积极性,我使用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()
  • 相关阅读:
    Django:RestFramework之-------视图
    Django:RestFramework之-------分页
    Django:RestFramework之-------序列化器
    Django:RestFramework之-------解析器
    Django:RestFramework之-------认证
    【WCF--初入江湖】02 WCF契约
    【WCF--初入江湖】01 WCF编程概述
    【WCF--初入江湖】目录
    【MongoDb--初入江湖】windows下安装MongoDb
    【Asp.Net-- 杂七杂八】的代码
  • 原文地址:https://www.cnblogs.com/shaosks/p/7084004.html
Copyright © 2020-2023  润新知