转载: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()

Python实现局域网内屏幕广播的技术要点分析(转载)的更多相关文章

  1. 混合事务分析处理“HTAP”的技术要点分析

    HTAP是近些年来比较火的一个概念,本文将聊聊HTAP的前世今生及技术特点. 一.数据应用类别 根据数据的使用特征,可简单做如下划分.在选择技术平台之前,我们需要做好这样的定位. 1.1 OLTP 联 ...

  2. 使用 Python 查看局域网内存活主机

    1 安装 (如果误用了 pip insatll nmap的话,要先 pip uninstall nmap) pip install python-nmap Nmap 是一款用于网络发现和安全审计的网络 ...

  3. 使用Python检测局域网内IP地址使用情况

    来源:https://www.cnblogs.com/donlin-zhang/p/6812675.html 在测试环境搭建的过程中,经常需要给服务器分配静态IP地址,由于不清楚当前局域网内部哪些IP ...

  4. Mysql 锁技术要点【转载】

    MyISAM和InnoDB的区别 MySQL默认采用的是MyISAM. MyISAM不支持事务,而InnoDB支持.InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事 ...

  5. 可以搜索局域网内的所有IP地址的软件

    几乎都用现有的工具,直接扫描,这里我已python为例,搜索一下局域网内所有活动IP,基本原理就是ping,对返回的结果进行分析,从而判断对应ip是否活动,代码很简单,实验环境win10+python ...

  6. Python一个命令开启http下载服务器(可以局域网内共享文件)

    前提条件:windows系统上安装python 如果想把D:\asdm作为提供下载的目录, 打开cmd然后cd命令进入该目录:cd D:\asdm, 然后执行Python -m SimpleHTTPS ...

  7. ICMP协议 广播以查询局域网内的所有主机

    看到了很多局域网内的主机扫描工具,在想怎么去实现这样一个工具.前几天看了Ping源码--ICMP协议的实例,ICMP可以用来探测网联网内的任一主机,ICMP和广播地址结合来扫描局域网内的所有主机不是很 ...

  8. wifi智能插座 一键扫描局域网内插座Ip及其它信息 Python源代码API

    转载请保留原地址. http://www.cnblogs.com/lsqandzy 最近买了几个智能插座玩,插座安装在家里,连接好wifi,不管你人在哪里,通过手机,一键开启或关闭插座电源,想象一下, ...

  9. 局域网内python socket实现windows与linux间简单的消息传送

    有个需求,就是在windows上看见一篇介绍linux相关的文章,想在局域网内的另外一台linux电脑上尝试一下, 于是就需要把该网页链接发送给linux,不想一点一点敲链接,又苦于没有找到其它好的方 ...

随机推荐

  1. ZigBee MAC层(下)

    6. MAC功能描述 6.1 信道访问 802.15.4使用的物理无线电信道的访问机制有下面两种: - 基于竞争的访问机制: 设备使用CSMA-CA退避算法以分布式方式访问信道 - 无竞争的访问机制: ...

  2. HTML布局相关的CSS样式属性

    # 转载请留言联系 注意,样式属性是写进CSS里面的. 布局常用样式属性: width 设置元素(标签)的宽度,如:width:100px; height 设置元素(标签)的高度,如:height:2 ...

  3. python gui tkinter用法杂记

    1.treeview遍历 iids = tree.selection() t = tree.get_children() for i in t: print(tree.item(i,'values') ...

  4. 【UOJ UNR #1】火车管理 可持久化线段树

    用可持久化线段树维护每个站的第一辆车和每个站的前一次更新的位置即可. #include<iostream> #include<cstring> #include<cstd ...

  5. (五)cobbler自定义系统安装

    注意:需要提前获取到物理机对应的网卡的MAC地址,例如我这里使用虚拟机进行演示 cobbler system add --name=linux-node2.com --mac=00:50:56:22: ...

  6. Linux下Tomcat安装配置

    买了台阿里云服务器,因为配置比较低,所以用Linux系统,这里记录一下我在Linux系统中Tomcat的安装配置. 前提JDK已经安装好. 安装 首先在/usr/local/下建立一个tomcat的文 ...

  7. 计蒜客 30996.Lpl and Energy-saving Lamps-线段树(区间满足条件最靠左的值) (ACM-ICPC 2018 南京赛区网络预赛 G)

    G. Lpl and Energy-saving Lamps 42.07% 1000ms 65536K   During tea-drinking, princess, amongst other t ...

  8. MySQL数据库增删改字段(属性)

    MySQL数据库的各种操作今天在这里总结一下: 一.增加 1.在已有的表中添加新的字段: 首先是增加表的字段,比如一张表原本没有字段“ Time ”,现在我们要增加这样一个字段,可以用下面的SQL语句 ...

  9. Linux命令之fdisk

    fdisk fdisk [选项] <disk>  改变分区表 fdisk [选项] –l <disk>  列出所有分区表 fdisk –s <partition(分区编号 ...

  10. sqlldr Field in data file exceeds maximum length "

    使用sqlldr导数时出现如下错误: " Record 1: Rejected - Error on table PC_PLANNAME, column PLANNAME.Field in ...