【Python】基于socket函数的TCP通信

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 tcpclient
    except:
        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))
            break
        tcpclient.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 = 0
                save_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 = filesize
                    fp.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~100
        frame_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'):
            break
    cap.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 datetime


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 tcpclient
    except:
        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))
            break
        tcpclient.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~100
        frame_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'):
            break
    cap.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.1
    alian_ubuntu:192.168.2.42
    alian_window:192.168.2.36
    """
    host = "192.168.2.36"
    port = 6666
    for 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 struct

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

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 = 0
                save_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 = filesize
                    fp.write(data)  # 写入文件数据
                fp.close()
            print("-" * 5 + "接收完成" + "-" * 5)
            sock.close()
            break

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

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

if __name__=="__main__":
    host,port = '192.168.2.36', 6666
    tcpserver = TCPSever(host,port)
    # 3.接收数据
    # 3.1 接收文件函数
    ReceiveFile(tcpserver)
    # 3.2 接收图片函数
    # ReceiveImg(tcpserver)
    # 3.3 接收视频帧
    # ReceiveFrame(tcpserver)
    # 4.关闭服务端
    tcpserver.close()
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值