基于socket函数的TCP通信
- 1 引言
- 1.1 通信协议函数(客户端)
- 1.2 通信协议函数(服务端)
- 2 传输文件(以txt文件为例)
- 2.1 发送端(客户端)
- 2.2 接受端(服务端)
- 3 传输图片(基于OpenCV)
- 3.1 发送端(客户端)
- 3.2 接受端(服务端)
- 4 传输视频帧(以默认摄像头为例)
- 4.1 发送端
- 4.2 接收端
- 5 应用实例
- 5.1 客户端
- 5.2 服务端
1 引言
TCP协议,传输控制协议(Transmission Control Protocol,TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC793定义。
参考链接:
🔗Python struct.pack用法介绍
🔗OpenCV实现图像网络传输的基本步骤
1.1 通信协议函数(客户端)
def TCPClient(host,port): # 通信协议函数(客户端)"""发送文件(也可以发送图片,但是比较慢)"""time_now = time.strftime("%Y-%m-%d %H:%M:%S")# 1.创建一个客户端的socket对象:AF_INET表示IPv4地址、SOCK_STREAM表示使用TCP协议进行通信tcpclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:# 2.设置要连接的服务端的ip和端口tcpclient.connect((host, port))print('%s 服务器已连接'%time_now)return tcpclientexcept:print('服务器连接失败,请修改后重新运行!!')exit(0)
1.2 通信协议函数(服务端)
def TCPSever(host,port): # 通信协议函数(服务端)time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))# 1.创建一个客户端的socket对象tcpserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP# 2.绑定端口:tcpserver.bind((host,port))print("%s 服务端已开启" % time_now)return tcpserver
2 传输文件(以txt文件为例)
2.1 发送端(客户端)
def SendFile(tcpclient, file):"""发送文件(也可以发送图片,但是比较慢)"""fhead = struct.pack(b'128sq', bytes(os.path.basename(file), encoding='utf-8'),os.stat(file).st_size) # 将图片路径和大小打包成一个二进制字符串tcpclient.send(fhead)fp = open(file, 'rb') # 打开要传输的文件(二进制只读模式)while True:data = fp.read(1024) # 读入图片数据(每次读取1KB)if not data:print('{0} send over...'.format(file))breaktcpclient.send(data) # 以二进制格式发送文件数据============================================>
2.2 接受端(服务端)
def ReceiveFile(tcpserver):"""循环接收文件"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128sq')print(fileinfo_size)buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename, filesize = struct.unpack('128sq', buf) # 解包recvd_size = 0save_name = filename.decode().strip('\x00') # 文件名解码print(f"文件名:{save_name}")fp = open(save_name, 'wb')while not recvd_size == filesize:if filesize - recvd_size > 1024:data = sock.recv(1024)recvd_size += len(data)else:data = sock.recv(1024)recvd_size = filesizefp.write(data) # 写入文件数据fp.close()print("-" * 5 + "接收完成" + "-" * 5)sock.close()break
3 传输图片(基于OpenCV)
3.1 发送端(客户端)
def SendImgArray(tcpclient,file):"""发送图片(OpenCV读取的图像数组)"""img = cv2.imread(file)# 图片编码params = [cv2.IMWRITE_JPEG_QUALITY, 100] # 指定JPEG的图像质量,100(最高质量)img_encode = cv2.imencode('.jpg', img, params)[1]data_encode = np.array(img_encode)data = data_encode.tobytes()len_data = len(data)fhead = struct.pack(b'128siiiiq',bytes(os.path.basename(file), encoding='utf-8'),1263,627,1459,1073,len_data) # 将图片路径和大小打包成一个二进制字符串# 3.发送数据tcpclient.send(fhead)tcpclient.send(data) # 发送图片============================================================>
3.2 接受端(服务端)
def ReceiveImg(tcpserver):"""循环接收图片"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128siiiiq')buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename,x1,y1,x2,y2,filesize = struct.unpack('128siiiiq', buf) # 解包save_name = filename.decode().strip('\x00') # 文件名解码print(f"图片名称:{save_name}")print(f"目标检测坐标:{x1,y1,x2,y2}") # 附加信息,可以自定义data = b''while len(data)<filesize:data += sock.recv(1024)img_decode = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR)# 显示图像cv2.imshow('Image', img_decode)cv2.waitKey(500) # 显示时长根据传输的速度自定义# 保存图像cv2.imwrite(save_name,img_decode)print("-" * 5 + "接收完成" + "-" * 5)cv2.destroyAllWindows()break
4 传输视频帧(以默认摄像头为例)
4.1 发送端
def SendFrame(host, port,add=0): # 仅传视频cap = cv2.VideoCapture(add) # 生成读取摄像头对象# 定义视频对象输出# width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 获取视频的宽度# height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 获取视频的高度# fps = cap.get(cv2.CAP_PROP_FPS) # 获取视频的帧率# fourcc = int(cap.get(cv2.CAP_PROP_FOURCC)) # 视频的编码# writer = cv2.VideoWriter("video_result.mp4", fourcc, fps, (width, height))while cap.isOpened():tcpclient = TCPClient(host, port)ret, frame = cap.read() # 读取摄像头画面hm = int(round(time.time() * 1000))frame_now = datetime.datetime.fromtimestamp(hm/1000).strftime("%Y%m%d%H%M%S%f")# 图像压缩frame = cv2.resize(frame, (192, 108))# 图像编码params = [cv2.IMWRITE_JPEG_QUALITY, 100] # ratio:0~100frame_encode = cv2.imencode('.jpg', frame, params)[1] # 图像编码frame_encode = np.array(frame_encode)frame_encode = frame_encode.tobytes()# 3.发送数据len_data = len(frame_encode)fhead = struct.pack(b'128sq',bytes(frame_now, encoding='utf-8'),len_data) # 将图片路径和大小打包成一个二进制字符串# 3.发送数据tcpclient.send(fhead)tcpclient.send(frame_encode) # 发送图片============================================================# 显示画面cv2.imshow('Map', frame)key = cv2.waitKey(24)# writer.write(frame) #视频保存# 按Q退出if key == ord('q'):breakcap.release() # 释放摄像头cv2.destroyAllWindows() # 释放所有显示图像窗口
4.2 接收端
def ReceiveFrame(tcpserver):"""循环接收视频帧"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128sq')buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename,filesize = struct.unpack('128sq', buf) # 解包save_name = filename.decode().strip('\x00') # 文件名解码print(f"图片名称:{save_name}")data = b''while len(data)<filesize:data += sock.recv(1024)img_decode = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR)# 显示图像cv2.imshow('Image', img_decode)cv2.waitKey(500) # 显示时长根据传输的速度自定义# 保存图像# cv2.imwrite(save_name,img_decode)print("-" * 5 + "接收完成" + "-" * 5)cv2.destroyAllWindows()break
5 应用实例
完整代码如下:
5.1 客户端
# -*- coding: utf-8 -*-
"""
2023.12.28
author:alian
function
发送端:车载服务器
1.传输文件
2.传输图片
3.传输视频帧
"""
import os.path
import socket
import cv2
import numpy as np
import time
import struct
import glob
import datetimedef TCPClient(host,port): # 通信协议函数(客户端)"""发送文件(也可以发送图片,但是比较慢)"""time_now = time.strftime("%Y-%m-%d %H:%M:%S")# 1.创建一个客户端的socket对象:AF_INET表示IPv4地址、SOCK_STREAM表示使用TCP协议进行通信tcpclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)try:# 2.设置要连接的服务端的ip和端口tcpclient.connect((host, port))print('%s 服务器已连接'%time_now)return tcpclientexcept:print('服务器连接失败,请修改后重新运行!!')exit(0)def SendFile(tcpclient, file):"""发送文件(也可以发送图片,但是比较慢)"""fhead = struct.pack(b'128sq', bytes(os.path.basename(file), encoding='utf-8'),os.stat(file).st_size) # 将图片路径和大小打包成一个二进制字符串tcpclient.send(fhead)fp = open(file, 'rb') # 打开要传输的文件(二进制只读模式)while True:data = fp.read(1024) # 读入图片数据(每次读取1KB)if not data:print('{0} send over...'.format(file))breaktcpclient.send(data) # 以二进制格式发送文件数据============================================># 4.关闭客户端tcpclient.close()def SendImgArray(tcpclient,file):"""发送图片(OpenCV读取的图像数组)"""img = cv2.imread(file)# 图片编码params = [cv2.IMWRITE_JPEG_QUALITY, 100] # 指定JPEG的图像质量,100(最高质量)img_encode = cv2.imencode('.jpg', img, params)[1]data_encode = np.array(img_encode)data = data_encode.tobytes()len_data = len(data)fhead = struct.pack(b'128siiiiq',bytes(os.path.basename(file), encoding='utf-8'),1263,627,1459,1073,len_data) # 将图片路径和大小打包成一个二进制字符串# 3.发送数据tcpclient.send(fhead)tcpclient.send(data) # 发送图片============================================================># 4.关闭客户端tcpclient.close()def SendFrame(host, port,add=0): # 仅传视频cap = cv2.VideoCapture(add) # 生成读取摄像头对象# 定义视频对象输出# width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 获取视频的宽度# height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 获取视频的高度# fps = cap.get(cv2.CAP_PROP_FPS) # 获取视频的帧率# fourcc = int(cap.get(cv2.CAP_PROP_FOURCC)) # 视频的编码# writer = cv2.VideoWriter("video_result.mp4", fourcc, fps, (width, height))while cap.isOpened():tcpclient = TCPClient(host, port)ret, frame = cap.read() # 读取摄像头画面hm = int(round(time.time() * 1000))frame_now = datetime.datetime.fromtimestamp(hm/1000).strftime("%Y%m%d%H%M%S%f")# 图像压缩frame = cv2.resize(frame, (192, 108))# 图像编码params = [cv2.IMWRITE_JPEG_QUALITY, 100] # ratio:0~100frame_encode = cv2.imencode('.jpg', frame, params)[1] # 图像编码frame_encode = np.array(frame_encode)frame_encode = frame_encode.tobytes()# 3.发送数据len_data = len(frame_encode)fhead = struct.pack(b'128sq',bytes(frame_now, encoding='utf-8'),len_data) # 将图片路径和大小打包成一个二进制字符串# 3.发送数据tcpclient.send(fhead)tcpclient.send(frame_encode) # 发送图片============================================================# 显示画面cv2.imshow('Map', frame)key = cv2.waitKey(24)# writer.write(frame) #视频保存# 按Q退出if key == ord('q'):breakcap.release() # 释放摄像头cv2.destroyAllWindows() # 释放所有显示图像窗口if __name__ == "__main__":file_path = '/media/ll/AI-2/20231225-bjdt/Camera/Camera_1226/results_txt' # 路径file_list = glob.glob('%s/*'%file_path) # 文件列表"""车载ip:20.198.147.136地面ip:172.16.77.1alian_ubuntu:192.168.2.42alian_window:192.168.2.36"""host = "192.168.2.36"port = 6666for file in file_list:tcpclient = TCPClient(host,port)# 3.发送数据# 3.1 发送文件SendFile(tcpclient, file)# 3.2 发送图片# SendImgArray(tcpclient, file)# 3.3 发送视频帧(接收端与图片接收端一致)# SendFrame(host, port, add=0)
5.2 服务端
#coding:utf-8
"""
2023-12-28
author:alian
function
接收端:地面服务器
1.循环接收文件
2.循环接收图片
3.循环接收视频帧
"""
import socket
import cv2
import numpy as np
import time
import structdef TCPSever(host,port): # 通信协议函数(服务端)time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))# 1.创建一个客户端的socket对象tcpserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP# 2.绑定端口:tcpserver.bind((host,port))print("%s 服务端已开启" % time_now)return tcpserverdef ReceiveFile(tcpserver):"""循环接收文件"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128sq')print(fileinfo_size)buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename, filesize = struct.unpack('128sq', buf) # 解包recvd_size = 0save_name = filename.decode().strip('\x00') # 文件名解码print(f"文件名:{save_name}")fp = open(save_name, 'wb')while not recvd_size == filesize:if filesize - recvd_size > 1024:data = sock.recv(1024)recvd_size += len(data)else:data = sock.recv(1024)recvd_size = filesizefp.write(data) # 写入文件数据fp.close()print("-" * 5 + "接收完成" + "-" * 5)sock.close()breakdef ReceiveImg(tcpserver):"""循环接收图片"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128siiiiq')buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename,x1,y1,x2,y2,filesize = struct.unpack('128siiiiq', buf) # 解包save_name = filename.decode().strip('\x00') # 文件名解码print(f"图片名称:{save_name}")print(f"目标检测坐标:{x1,y1,x2,y2}") # 附加信息,可以自定义data = b''while len(data)<filesize:data += sock.recv(1024)img_decode = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR)# 显示图像cv2.imshow('Image', img_decode)cv2.waitKey(500) # 显示时长根据传输的速度自定义# 保存图像cv2.imwrite(save_name,img_decode)print("-" * 5 + "接收完成" + "-" * 5)cv2.destroyAllWindows()breakdef ReceiveFrame(tcpserver):"""循环接收图片"""# 设置监听tcpserver.listen(5)while True:# 3.接收数据:accept()函数会返回一个元组, 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)sock, addr = tcpserver.accept()print("Accept connection from {0}".format(addr)) # 查看发送端的ip和端口print("-" * 5 + "开始接收" + "-" * 5)while True:fileinfo_size = struct.calcsize('128sq')buf = sock.recv(fileinfo_size) # 接收图片名if buf:filename,filesize = struct.unpack('128sq', buf) # 解包save_name = filename.decode().strip('\x00') # 文件名解码print(f"图片名称:{save_name}")data = b''while len(data)<filesize:data += sock.recv(1024)img_decode = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR)# 显示图像cv2.imshow('Image', img_decode)cv2.waitKey(500) # 显示时长根据传输的速度自定义# 保存图像# cv2.imwrite(save_name,img_decode)print("-" * 5 + "接收完成" + "-" * 5)cv2.destroyAllWindows()breakif __name__=="__main__":host,port = '192.168.2.36', 6666tcpserver = TCPSever(host,port)# 3.接收数据# 3.1 接收文件函数ReceiveFile(tcpserver)# 3.2 接收图片函数# ReceiveImg(tcpserver)# 3.3 接收视频帧# ReceiveFrame(tcpserver)# 4.关闭服务端tcpserver.close()