引言
最近工作需要实现将树莓派处理的信号文件与工控机进行通信,然后将文件传输到工控机,并且传输完一个就删除一个。所以想着利用socket通信初步模拟出来一个流程,等待之后再将它部署到具体的树莓派和工控机下。主要利用的是python的socket库,主要是将树莓派作为服务器端,工控机作为客户端,树莓派一旦监测到指定文件夹下有文件产生就进行通信传输。
实现
1.服务器端:(第一种监听方式)
(1)主要是进行连接询问建立后,通过python的watchdog库实时监控指定文件夹是否有“文件生成”的事件发生,一旦有,就进行send_file。并且为了防止文件正在生成而被发送,采用的是利用0.2秒来检查是否有文件大小变化的情况。
(2)每个文件结束后都标注了“b'END_OF_FILE_MARKER\r\n'”,以此来防止提前传入下一个文件的内容。
import socket
import os
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
WATCHED_FOLDER = 'E:\\network_socket\\test_network'
HOST = 'localhost'
PORT = 8888
CHECK_INTERVAL = 0.2 # 以秒为单位,检查文件大小变化的间隔时间
class FileHandler(FileSystemEventHandler):
def __init__(self, conn):
self.conn = conn
def on_created(self, event):
if not event.is_directory:
self.wait_for_file_completion(event.src_path)
def wait_for_file_completion(self, file_path):
initial_size = os.path.getsize(file_path)
time.sleep(CHECK_INTERVAL) # 等待一段时间,让文件有时间完成生成
final_size = os.path.getsize(file_path)
# 检查文件大小是否稳定
while final_size != initial_size:
initial_size = final_size
time.sleep(CHECK_INTERVAL)
final_size = os.path.getsize(file_path)
# 文件生成完成后发送文件
self.send_file(file_path)
def send_file(self, file_path):
try:
file_name = os.path.basename(file_path)
self.conn.send(file_name.encode()) # 发送文件名
self.conn.sendall(b'\r\n')
with open(file_path, 'rb') as f:
while True:
data = f.read(1024)
if not data:
break
self.conn.sendall(data) # 发送文件内容
# 发送文件结束标志
self.conn.sendall(b'END_OF_FILE_MARKER\r\n')
print(f"File {file_path} has been sent")
os.remove(file_path) # 发送完成后删除文件
print(f"File {file_path} has been deleted")
except Exception as e:
print(f"An error occurred while sending the file: {e}")
def server_program():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(1)
print("Server listening...")
conn, addr = server_socket.accept()
print(f"Connection from {addr} has been established!")
event_handler = FileHandler(conn)
observer = Observer()
observer.schedule(event_handler, path=WATCHED_FOLDER, recursive=False)
try:
observer.start()
except KeyboardInterrupt:
observer.stop()
except Exception as e:
print(f"An error occurred: {e}")
finally:
observer.join()
conn.close()
server_socket.close()
if __name__ == '__main__':
server_program()
服务器端:(第二种监听方式)
采用轮询的方式去访问文件,并按照时间戳大小排列进行读取。
import os
import socket
import time
WATCHED_FOLDER = 'E:\\network_socket\\test_network'
HOST = 'localhost'
PORT = 8888
BUFFER_SIZE = 4096
POLL_INTERVAL = 1 # seconds
def get_sorted_files_by_name(folder_path):
files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if
os.path.isfile(os.path.join(folder_path, f))]
return sorted(files, key=lambda f: os.path.basename(f))
def send_file(client_socket, file_path):
try:
file_name = os.path.basename(file_path)
client_socket.send(file_name.encode()) # 发送文件名
client_socket.sendall(b'\r\n')
with open(file_path, 'rb') as f:
while True:
data = f.read(1024)
if not data:
break
client_socket.sendall(data) # 发送文件内容
# 发送文件结束标志
client_socket.sendall(b'END_OF_FILE_MARKER\r\n')
print(f"File {file_path} has been sent")
os.remove(file_path) # 发送完成后删除文件
print(f"File {file_path} has been deleted")
except Exception as e:
print(f"An error occurred while sending the file: {e}")
def handle_client(client_socket):
try:
files = get_sorted_files_by_name(WATCHED_FOLDER)
for file_path in files:
send_file(client_socket, file_path)
print(f"Sent file: {file_path}")
print("All files sent.")
except Exception as e:
print(f"An error occurred: {e}")
def monitor_folder():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(1)
print("Server listening...")
conn, addr = server_socket.accept()
print(f"Connection from {addr} has been established!")
try:
while True:
handle_client(conn)
time.sleep(POLL_INTERVAL)
except Exception as e:
print(f"An error occurred: {e}")
finally:
conn.close()
server_socket.close()
if __name__ == '__main__':
monitor_folder()
2.客户端:将文件逐个保存到工控机下。其次还有可以直接传字符流,不用再存储文件,相关代码也很好改正,在此不必赘述,根据实际情况选择需求。
import socket
def client_program(client_socket):
try:
# 接收文件名
file_name = client_socket.recv(1024).decode()
print(file_name)
if not file_name:
raise Exception("No file name received111")
if file_name == 'END_OF_FILE_MARKER':
raise Exception("No file name received222")
# 接收文件数据
file_path = f'E:\\network_socket\\recv_file\\{file_name}' # 本地保存路径
with open(file_path, 'wb') as f:
while True:
data = client_socket.recv(1024)
if data.endswith(b'END_OF_FILE_MARKER\r\n'):
data = data[:-len(b'END_OF_FILE_MARKER\r\n')]
if data:
f.write(data)
break
if not data:
break
f.write(data)
print(f"File received and saved as {file_path}")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == '__main__':
host = 'localhost' # 服务器的IP地址
port = 8888 # 服务器的端口号
# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host, port)) # 连接到服务器
while True:
client_program(client_socket)
# client_socket.close()
对此,还在服务器端模拟了文件生成进行验证
import os
import time
def generate_files_in_folder(folder_path, interval=5):
"""
在指定文件夹中每隔一定时间生成一个文件。
:param folder_path: 文件夹的路径,用于存放生成的文件。
:param interval: 生成文件的时间间隔(秒)。
"""
if not os.path.exists(folder_path):
print(f"指定的文件夹不存在: {folder_path}")
return
while True:
# 生成一个唯一的文件名(这里使用时间戳和随机数)
file_name = f"file_{int(time.time())}_{os.urandom(4).hex()}.txt"
file_path = os.path.join(folder_path, file_name)
# 创建文件并写入一万个数
with open(file_path, 'w') as file:
for i in range(10000):
file.write(f"{i}\n")
print(f"已创建文件: {file_path}")
# 等待指定的时间间隔
time.sleep(interval)
# 示例用法
folder_path = "E:\\network_socket\\test_network" # 请替换为你的文件夹路径
generate_files_in_folder(folder_path, 1) # 每隔5秒生成一个文件
运行结果
开始:
1.先运行server.py
2.再运行client.py
3.再开始创建文件