ubuntu下使用docker和socket进行数据交互记录
概述:主要实现了在宿主机上通过8000端口传递一张图像给docker镜像,然后镜像中处理后,通过8001端口回传处理后的图像给宿主机。
第一章、构建镜像
一、dockerfile文件
1.拉取ubuntu20.04镜像
基于ubuntu20.04进行构建,拉取ubuntu20.04的指令如下:
sudo docker pull ubuntu:20.04
2.编写dockerfile文件
dockerfile写入如下:
# Dockerfile
# Base images 基础镜像
FROM ubuntu:20.04
# MAINTAINER 维护者信息
maintainer chenjun_1241370589@qq.com
# 设置超时
ENV PIP_DEFAULT_TIMEOUT=100
# 设置环境变量以避免手动选择时区
ENV DEBIAN_FRONTEND=noninteractive
#RUN 执行以下命令
RUN apt update
RUN apt install python3 python3-pip -y
RUN apt-get install nano
# 安装必要的系统依赖项
RUN apt-get update && apt-get install -y \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender-dev
RUN pip3 install --upgrade pip setuptools
RUN pip3 cache purge
RUN pip3 install opencv-python==4.5.5.62 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装 OpenCV
RUN apt-get update && apt-get install -y \
libgl1-mesa-glx # 安装 OpenGL 库
# 其他依赖和设置
RUN pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN apt install libgl1-mesa-glx
RUN mkdir -p /data/code/
#拷贝文件至工作文件夹
COPY test.py /data/code/test.py
#工作目录
WORKDIR /data/code/
#容器启动时执行的命令
CMD ["python3","test.py"]
二、编写docker内运行文件
1.新建test.py文件
test.py文件将被拷贝进镜像中,作为镜像内部运行文件。写入如下:
import cv2 # 导入OpenCV库
import numpy as np # 导入NumPy库
import socket # 导入socket库
import struct # 导入struct库
import time
def receive_image():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字
server_socket.bind(('0.0.0.0', 8000)) # 绑定到本地地址和端口8000
server_socket.listen(1) # 监听连接
print("Waiting for connection...") # 打印等待连接信息
client_socket, addr = server_socket.accept() # 接受连接
print(f"Connected to {addr}") # 打印连接地址
data = b"" # 初始化数据变量
payload_size = struct.calcsize("L") # 获取消息大小的字节数
while len(data) < payload_size: # 接收消息大小
data += client_socket.recv(4096)
packed_msg_size = data[:payload_size] # 获取打包的消息大小
data = data[payload_size:] # 剩余数据
msg_size = struct.unpack("L", packed_msg_size)[0] # 解包消息大小
while len(data) < msg_size: # 接收完整消息
data += client_socket.recv(4096)
frame_data = data[:msg_size] # 获取图像数据
frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) # 解码图像
client_socket.close() # 关闭客户端套接字
server_socket.close() # 关闭服务器套接字
return frame # 返回图像
def process_image(image):
height, width = image.shape[:2] # 获取图像高度和宽度
new_height, new_width = height // 2, width // 2 # 计算新高度和宽度
print("Process_image ok !")
return cv2.resize(image, (new_width, new_height)) # 调整图像大小
def send_image(image):
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字
for _ in range(5): # 尝试5次连接
try:
client_socket.connect(('172.17.0.1', 8001)) # 连接到指定地址和端口
break
except ConnectionRefusedError:
print("Connection refused, retrying...")
time.sleep(2) # 等待2秒后重试
else:
print("Failed to connect after 5 attempts")
return
_, img_encoded = cv2.imencode('.jpg', image) # 编码图像为JPEG格式
data = img_encoded.tobytes() # 转换为字节数据
client_socket.sendall(struct.pack("L", len(data)) + data) # 发送数据
client_socket.close() # 关闭客户端套接字
if __name__ == "__main__":
while True:
# 接收图像
received_image = receive_image()
# 处理图像
processed_image = process_image(received_image)
# 发送处理后的图像
send_image(processed_image)
其中client_socket.connect((‘172.17.0.1’, 8001)) # 连接到指定地址和端口这一行中的ip需要在宿主机运行指令:
ip addr show docker0
将inet后的ip填入代码中。
三、构建镜像
宿主机终端输入:
sudo docker build -t python_socket2 . -f Dockerfile
其中“python_socket2”是我自己创建的名字,自行修改。
第二章、宿主机代码
一、交互代码
在宿主机中写入test1.py代码,这里图像处理部分只对图像进行尺寸减半的操作,代码如下:
import cv2
import numpy as np
import socket
import struct
def send_image(image_path):
# 读取图像
image = cv2.imread(image_path)
if image is None:
print(f"无法读取图像: {image_path}")
return
# 创建TCP/IP套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 8000)) # 连接到Docker容器的8000端口
# 编码图像为JPEG格式
_, img_encoded = cv2.imencode('.jpg', image)
data = img_encoded.tobytes()
# 发送数据
client_socket.sendall(struct.pack("L", len(data)) + data)
client_socket.close()
print("图像已发送")
def receive_processed_image():
# 创建TCP/IP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8001)) # 绑定到本地地址和端口8001
server_socket.listen(1)
print("等待接收处理后的图像...")
client_socket, addr = server_socket.accept()
print(f"已连接到 {addr}")
data = b""
payload_size = struct.calcsize("L")
while len(data) < payload_size:
data += client_socket.recv(4096)
packed_msg_size = data[:payload_size]
data = data[payload_size:]
msg_size = struct.unpack("L", packed_msg_size)[0]
while len(data) < msg_size:
data += client_socket.recv(4096)
frame_data = data[:msg_size]
# 解码图像
frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR)
client_socket.close()
server_socket.close()
return frame
if __name__ == "__main__":
image_path = "/home/cj/work/Docker/Yolov5DnnImage/bus.jpg" # 替换为实际的图像路径
send_image(image_path)
processed_image = receive_processed_image()
# 显示处理后的图像
#cv2.imshow("Processed Image", processed_image)
#cv2.waitKey(0)
#cv2.destroyAllWindows()
# 保存处理后的图像
cv2.imwrite("processed_image.jpg", processed_image)
print("处理后的图像已保存为 processed_image.jpg")
图像输入地址自行修改。
第三章、交互使用
一、docker端
在宿主机终端输入:
sudo docker run -p 8000:8000 -i -t python_socket2:latest
这里的“python_socket2:latest”修改成自己的镜像。
查看镜像宿主机终端输入:
sudo docker images
二、宿主机端
运行test1.py文件
同时在docker端也会有输出:
在宿主机当前工作目录下也会生成对应的图像。