目的
博文目的:分享这个不成熟的小案例,希望能给还在tkinter控件整合上没有思路的同志一些参考。
案例目的:在无法访问互联网的情况下,实现一个局域网内机器传输文件和文本的工具
效果图
实现
环境:Python 3.7 windows
上面是我在本机运行两个同样的脚本,进行文本或文件互发的效果,这里运用到下面几个点:
- 信息对话框和文件选择框
- 控件值的获取和值的更新
- 界面的大小变化(比较原始界面和点击setup之后延展出来的界面)
- 基本socket服务端与客户端
- 线程规避tkinter卡顿问题
内容发送与接收
关于内容的发送与接收,脚本主要运用socket套接字实现客户端或服务器端,点击按钮[启动监听]的过程实际上是程序创建一个监听指定端口的服务端,而发送内容的过程则是向目标程序的服务端发起交互的过程
另一个问题是对于文件和文本的接收处理方式是不同的,如果接收到的是文本,我们要将文本内容插入文本框显示,如果是文件则要做保存文件的操作。因此我们需要让服务端socket判断接收到的内容属于哪种类型,需要在客户端socket发送时带上类型标志位,服务端socket接收到时就可以根据标志位做不同的行为。
下面是客户端在发送文本和文件时的区别
# 发送文本时:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect((host,port))
client.send(bytes('#msg#',encoding='utf-8'))
client.send(bytes(msg,encoding='utf-8'))
client.close()
# 发送文件时:
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect((host,port))
client.send(bytes(f'#file#{filename}#',encoding='utf-8'))
client.send(content)
可以看到在发送内容之前,我附带上了类型字段,这样我们的服务端socket接收到内容时就可以做下面的解析
recv_data = conn.recv(1024)
if recv_data[1] == 102:
endindex = recv_data.find(b'#',6)
filename = recv_data[6:endindex].decode('utf-8')
answer = messagebox.askyesno("Recv File",f"收到来自{addr[0]}主机的文件\n{filename}\n是否接收?")
if not answer:
continue
savepath = os.path.join(self.path,filename)
with open(savepath,"wb") as f:
f.write(recv_data[endindex+1:])
while recv_data:
recv_data = conn.recv(1024)
f.write(recv_data)
messagebox.showinfo("Recv File","文件接收成功!")
elif recv_data[1] == 109:
msg = recv_data[5:].decode('utf-8').strip()
self.recvbox.insert(f'{self.index}.0',f'From:{addr[0]}\n')
self.recvbox.insert(f'{self.index+1}.0',f"{msg}\n")
self.index +=2
conn.close()
注:脚本中的102和109对应的是f和m的ascill码.
界面实现
避免点击按钮后等待时间过长,使界面无反应的卡顿感,可以使用线程解决
def thre