<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>未分类 &#8211; 编程技术记录</title>
	<atom:link href="https://blog.z6z8.cn/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.z6z8.cn</link>
	<description>世界你好!</description>
	<lastBuildDate>Wed, 10 Aug 2022 06:25:24 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>Python使用图片、音频等传输数据</title>
		<link>https://blog.z6z8.cn/2022/08/10/python%e4%bd%bf%e7%94%a8%e5%9b%be%e7%89%87%e3%80%81%e9%9f%b3%e9%a2%91%e7%ad%89%e4%bc%a0%e8%be%93%e6%95%b0%e6%8d%ae/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Wed, 10 Aug 2022 06:25:24 +0000</pubDate>
				<category><![CDATA[未分类]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1244</guid>

					<description><![CDATA[近期因为一些原因，从内网传输数据到外网的行为受到限制，便研究了些非正常手段进行数据传输 使用二维码 原理是在内 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>近期因为一些原因，从内网传输数据到外网的行为受到限制，便研究了些非正常手段进行数据传输</p>
<h1>使用二维码</h1>
<p>原理是在内网系统的屏幕上生成二维码，每秒刷新一次；在外网用拍照或者截屏的方式读取二维码图片，解码为数据。</p>
<p>单个QR码国际标准二维码，二进制模式最多可存储2953字节，2KB多一点，所以使用二维码进行数据传输其速率应该在几KB/S。</p>
<h2>生成二维码</h2>
<p>需要使用 <code>PIL(pillow)</code>图像库,<code>qrcode</code>二维码生成库，以及<code>tkinter</code>UI界面库。<code>tkinter</code>默认随Python一起安装。</p>
<pre><code class="language-python">from PIL import Image, ImageTk
import qrcode
import base64
import tkinter

def  generateTkImages(data):
    tkImgs = []
    for i in range(4):  # 单次生成4张二维码图片，这里需要自己根据屏幕适配
        content  = b'需要传输的数据' 

        #理论上不需要base64编码，但是实际测试使用pyzbar解码字节流二维码时会乱码，所以使用base64将字节流转为字符串
        bs = base64.b64encode(content)

        #创建二维码容器，指定单个区块尺寸和边框尺寸
        qr = qrcode.QRCode(box_size = 2,border = 2)
        #添加数据
        qr.add_data(bs)
        #生成二维码图片
        img = qr.make_image()

        #转换成tkinter支持的图像
        tkImg = ImageTk.PhotoImage(image = img)
        tkImgs.append(tkImg) # 使用数组存储，避免因为tkinter的bug只显示最后一张
    return tkImgs

class QrApp(tkinter.Frame):
    def __init__(self,master = None):
        super().__init__(master)
        self.master = master

        self.pack()
        self.labels = []
        for i in range(4):
            lb = tkinter.Label(self)
            lb.grid(row = int(i /2) , column = i % 2 ) # 2行 2 列
            self.labels.append(lb)

    def  test(self):
        tkImgs = generateTkImages(需要传输的数据)
        for i in range(4):
            self.labels[i].configure(image=tkImgs[i])
            self.master.update() #更新界面
            self.master.after(1000) # 保持1000毫秒

root = tkinter.Tk()
root.geometry('1030x550+0+0')
app = QrApp(root)
root.after(500,app.test)
root.mainloop()</code></pre>
<h2>二维码解码</h2>
<p>本文使用<code>pyzbar</code>解码，图片来自截屏</p>
<pre><code class="language-python">
from pyzbar import pyzbar
from PIL import ImageGrab
import base64

# 截屏
img = ImageGrab.grab()
#使用pyzbar扫描并解码
barcodes = pyzbar.decode(img,symbols=[pyzbar.ZBarSymbol.QRCODE])

for j in barcodes:
    data = j.data.decode("utf-8")
    data = base64.b64decode(data)
    print(data)
</code></pre>
<h1>使用音频</h1>
<p>使用音频传输数据的思路是，用波形的高低代表0和1 ，将数据转换成音频文件，并播放出来，然后录音，最后分析录音文件，并还原为数据。</p>
<p>理论上，44100采样率的音频每秒传输数据极限不超过 44100 / 8 = 5512字节，实际上音频播放时和录音时会有不同程度的音频比特丢失，所以实际传输速率会受影响；而为保证传输精度，单个数据比特需要占有将连续的相邻音频比特，也就是多个音频比特才能表示一个数据比特，所以实际传输速率会更低。</p>
<h2>生成Wave文件</h2>
<pre><code class="language-python">import wave

data = b"数据"
audioBits = []
for Byte in range(len(data)):
    for i in range(8):
        bit = (Byte >> (7-i)) & 1
        for k in range(30): #每30个音频比特表示一个数据比特，这里需要根据录音精度调整
            audioBits.append( 255 * bit ) 

wavPath = "wave音频文件存储路径"
with wave.open(wavPath,"wb") as fd:
    fd.setparams((1,1,44100,44100,"NONE","not compressed"))
    fd.writeframes(bits)</code></pre>
<h2>Windows 录音</h2>
<p>使用<code>pyaudio</code></p>
<pre><code class="language-python">import pyaudio
import wave

#获取录音设备序号
def findRecordingDeviceID(pd ):
        name = '立体声混音'
        for i in range(pd.get_device_count()):
            devInfo = pd.get_device_info_by_index(i)
            if devInfo['name'].find(name) >=0 and devInfo['hostApi'] == 0 :
                return i
        return -1

def record()
    pd = pyaudio.PyAudio()
    #查找设备
    dev_id = findRecordingDeviceID(pd)
    if dev_id < 0 :
        print('无法找到录音设备!')
        return

    #打开设备
    record_stream = pd.open(input_device_index=dev_id,
                        format=pyaudio.paInt16,
                        channels=1,
                        rate=44100,
                        input=True,
                        frames_per_buffer=1024)

    #循环读取流
    frames = []
    while(True):
        data = record_stream.read(1024)
        frames.append(data)
        if (len(frames) > 1000): # 设置停止条件，这里只是演示
            break

    #停止读取流  
    record_stream.stop_stream()
    #关闭流
    record_stream.close()
   #停止资源占用
    pd.terminate()

    wavPath = "wave音频文件存储路径"
    with wave.open(wavPath,"wb") as fd:
        fd.setparams((1,1,44100,44100,"NONE","not compressed"))
        fd.writeframes(b''.join(frames))</code></pre>
<h2>解码Wave为数据</h2>
<p>示意代码</p>
<pre><code class="language-python">wavPath = "wave音频文件存储路径"
song = wave.open(wavPath)
frame_bytes = song.readframes(song.getnframes())

# 需要先检测音频比特的有效开端，例如连续100个高波形作为开端，后面为有效传输

# 假设已经检测到有效传输
sz = len(frame_bytes)
offset = 0
bits = []
while sz >= 30:
    sum = 0
    for k in range(30):
        sum += frame_bytes[offset+k]
    if (sum / 30) > 50: # 这里认为大于50就是 bit 1
        bits.append(1)
    else:
        bits.append(0)
    sz -= 30
    offset += 30

把比特流转换为字节（略）</code></pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
