Python 实现基于UDP协议的局域网广播

一、发送端。

f.py,源码如下:

from time import sleep
from os import startfile
from zlib import compress
from threading import Thread
from socket import (socket,AF_INET,SOCK_DGRAM,SOL_SOCKET,SO_BROADCAST)
from tkinter import Tk,BooleanVar,Button,Label
from PIL.ImageGrab import grab
root = Tk()
root.title("屏幕广播发送端-小6哥")
# 初始尺寸和位置
root.geometry('320x60+500+209')
#两个方向都不允许缩放
root.resizable(False,False)
#缓冲区大小
BUFFER_SIZE = 60*1024
#控制是否正在发送的变量
sending = BooleanVar(root,value=False)
def send_image():
    sock = socket(AF_INET,SOCK_DGRAM)
    # 广播
    sock.setsockopt(SOL_SOCKET,SO_BROADCAST,True)
    IP ='255.255.255.255'# 发送地址为<广播>
    serverPort = 22222# 请自行选择
    
    # Windows系统因为特殊的原因要多一些代码
    import platform
    if platform.system() == 'Windows':
        with socket(AF_INET, SOCK_DGRAM) as c:
            c.connect(('114.114.114.114', 80))
            local_ip = c.getsockname()[0]
            sock.bind((local_ip, serverPort))
    
    while sending.get():
        # 屏慕截图
        im = grab()
        w, h = im.size
        # 把图像转换为字节串,压缩后传输,减少带宽占用
        im_bytes = compress(im.tobytes())
        # 通知大家开始发送一幅图像
        sock.sendto(b'start',(IP,serverPort))
        #分块发送,以免字节串太长无法发送
        for i in range(len(im_bytes)//BUFFER_SIZE+1):
            start = i * BUFFER_SIZE
            end = start + BUFFER_SIZE
            sock.sendto(im_bytes[start:end],(IP,serverPort))
            # 通知大家已发送完一幅图像
        sock.sendto(b'_over'+str((w,h)).encode(),(IP,serverPort))
        #0.5秒截图发送一次
        sleep(0.5)
    sock.sendto(b'close',(IP,serverPort))
    sock.close()
lbCopyRight = Label(root,text='小6哥开发,关注微信公众号“暂无”查看源码识',fg='red',cursor='plus')
lbCopyRight.place(x=5,y=5,width=310,height=20)
url = r'https://blog.csdn.net/liujava56?spm=1011.2266.3001.5343'
lbCopyRight.bind("<Button-1>",lambda e: startfile(url))
def btnStartClick():
    sending.set(True)
    Thread(target=send_image).start()
    btnStart['state'] = 'disabled'
    btnStop['state'] = 'normal'
btnStart = Button(root,text='开始广播',command=btnStartClick)
btnStart.place(x=30,y=30,width=125,height=20)
def btnStopClick():
    sending.set(False)
    btnStart['state'] = 'normal'
    btnStop['state'] ='disabled'
btnStop = Button(root,text='停止广播',command=btnStopClick)
btnStop['state'] ='disabled'
btnStop.place(x=165,y=30,width=125,height=20)

root.mainloop()

二、接收端

s.py,源码如下:

from time import sleep
from os import startfile
from zlib import decompress
from threading import Thread
from tkinter import Tk,Canvas,BOTH,YES,Label
from socket import socket,AF_INET,SOCK_DGRAM
from PIL.Image import frombytes
from PIL.ImageTk import PhotoImage
#url = r'https://blog.csdn.net/liujava56?spm=1011.2266.3001.5343'
#startfile(url)
root = Tk()
root.title('屏幕广播接收端-小6哥-微信公众号“暂无”')
root.geometry('800x600+0+0')
# root.overrideredirect(True)
root.attributes('-topmost',True)
#使用Labe1显示图像,自带双缓冲,避免图像闪烁
lbImage = Label(root)
lbImage.pack(fill=BOTH,expand=YES)
BUFFER_SIZE = 60 *1024
data =[]
def show_image(image_bytes, image_size):
    #窗口尺寸
    screen_width = root.winfo_width()
    screen_height = root.winfo_height()
    # 必须使用全局变量,否则会不显示图像
    global im
    #重建图像,如果接收图像数据不完整就直接放弃
    try:
        im = frombytes('RGB',image_size,image_bytes)
    except:
        return
    # 显示图像
    im = im.resize((screen_width,screen_height))
    im = PhotoImage(im)
    lbImage['image'] = im
    lbImage.image = im
def recv_image():
    global receiving,im
    # 创建UDP套接字,绑定用来接收屏幕广播的端口
    sock = socket(AF_INET,SOCK_DGRAM)
    serverPort = 22222
    sock.bind(('',serverPort))
    
    while receiving:
        # 开始接收图像数据
        try:
            chunk,_= sock.recvfrom(BUFFER_SIZE)
        except:
            # 收到不完整图像,直接丢弃
            data.clear()
            continue
        # 等待接收开始标志
        # 防止半路加入的客户端从中间开始接收,导致图像不完整
        # 收到开始标志
        if chunk == b'start':
            data.clear()
            continue
        #一幅图像传输结束,重建图像并显示
        elif chunk.startswith(b'_over'):
            image_size = eval(chunk[5:])
            try:
                image_data = decompress(b''.join(data))
            except:
                # 收到不完整图像,直接丢弃
                data.clear()
                continue
            
            global thread_show
            # 创建线程并启动
            thread_show = Thread(target=show_image,args=(image_data,image_size))
            thread_show.daemon =True
            thread_show.start()
            
            data.clear()
            continue
        elif chunk == b'close':
            #receiving = False
            #sock.close()
            sleep(0.1)
            #root.destroy()
        data.append(chunk)

receiving = True
thread_sender = Thread(target=recv_image)
thread_sender.daemon = True
thread_sender.start()

#退出程序时,如果正在接收就停止接收再退出,保证线程安全
def close_window():
    global receiving
    receiving = False
    sleep(0.3)
    root.destroy()
root.protocol('WM_DELETE_WINDOW',close_window)
root.mainloop()

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值