TCP 网络通讯进行大文件无损传输

TCP 网络通讯进行大文件无损传输

TCP 传输小文件数据很简单,一次读取发送就OK了,但是大文件以及像多媒体数据除了要解决多次传输数据的数据读取合成还要解决编码问题;废话不多说直接上干货。

---- 亲测传输文本文件、图片、视频多媒体完美运行!!

TCP文件下载器-服务端-大文件版本

from socket import *
from tcp_transport_config import filler, valid_data_fixed_len, record_filler_len, data_package_size
import logging
import base64

logging.basicConfig(format='[%(name)s] %(levelname)s: %(message)s', level=logging.INFO)
logger = logging.getLogger("服务器")


def get_package_from_file(reader):
    real_data = reader.read(valid_data_fixed_len)
    if not real_data:
        return None

    # 获取读取到的数据长度,不足使用填充符填充
    current_data_len = len(real_data)

    # 获取填充数据
    fill_len = valid_data_fixed_len - current_data_len
    fill_data = (fill_len * filler).encode()

    # record_data 用来记录数据包的信息
    record_data = f"{fill_len:0>{record_filler_len}d}".encode()

    send_data = real_data + fill_data + record_data
    logger.debug(f"待发送数据包的 真实数据-填充-填充字符长度分别是: {len(real_data)} - {len(fill_data)} - {len(record_data)}")

    # 将768个字节 utf8编码数据  转换1024个字节 base64编码数据
    data_package = base64.b64encode(send_data)
    return data_package


def send_file_content(tcp_socket):
    file_name = tcp_socket.recv(data_package_size).decode()
    try:
        with open(file_name, "rb") as rf:
            logger.info(f"正在加载文件 {file_name}.....")
            while True:
                # 读取文件数据包
                data_package = get_package_from_file(rf)
                if not data_package:
                    logger.info(f"{file_name} 传输完成!!")
                    break
                tcp_socket.send(data_package)
                logger.debug(f"数据包为{data_package} 长度:{len(data_package)}")
    except Exception:
        logger.warning(f"文件 {file_name} 不存在!")


def main():
    tcp_server_socket = socket(AF_INET, SOCK_STREAM)
    tcp_server_socket.bind(("192.168.0.178", 8000))
    tcp_server_socket.listen(128)
    while True:
        tcp_socket, client_addr = tcp_server_socket.accept()
        logger.info(f"正在为{client_addr}服务!!")

        send_file_content(tcp_socket)
        tcp_socket.close()
        logger.info(f"为{client_addr}服务结束!!")

    tcp_server_socket.close()


if __name__ == '__main__':
    main()

TCP文件下载器-客户端-大文件版本

from socket import *
from tcp_transport_config import record_filler_len, data_package_size
import logging
import base64
import io

logging.basicConfig(format='[%(name)s] %(levelname)s: %(message)s', level=logging.INFO)
logger = logging.getLogger("客户端")


def write_package_to_file(writer, data_package):
    # 将1024个字节 base64编码数据解码为 768个字节utf8数据
    recv_data = base64.b64decode(data_package)
    logger.debug(f"接收的数据为: {recv_data}")
    logger.debug(f"接收的数据长度为: {len(recv_data)}")

    # 字节流读取数据
    recv_data_stream = io.BytesIO(recv_data)
    # 返回一个对应于缓冲区内容的可读写视图而不必拷贝其数据
    recv_data_stream = recv_data_stream.getvalue()

    # 获取填充字符的长度和记录填充字符所占用的长度 根据字节截取想要的数据
    fill_char_len = recv_data_stream[-record_filler_len:].decode()
    cut_len = int(fill_char_len) + record_filler_len
    recv_real_data = recv_data[:-cut_len]
    # 返回数据包的字节数据
    writer.write(recv_real_data)


def main():
    tcp_client_socket = socket(AF_INET, SOCK_STREAM)
    tcp_client_socket.connect(("192.168.0.178", 8000))

    # 客户输入要下载的文件名, 设置文件下载的保存路径
    download_filename = input("please enter file name: ")
    save_filename = "copy_" + download_filename
    # 发送需要下载文件名到服务器
    tcp_client_socket.send(download_filename.encode())

    data_package = tcp_client_socket.recv(data_package_size)
    logger.debug(f"接收的第一个编码数据包长度: {len(data_package)}")

    if data_package:
        with open(save_filename, "wb") as wf:
            while True:
                write_package_to_file(wf, data_package)

                data_package = tcp_client_socket.recv(data_package_size)
                # 文件传输结束
                if not data_package:
                    logger.info(f"{download_filename} 下载完成!!")
                    break

    tcp_client_socket.close()


if __name__ == '__main__':
    main()

配置文件 tcp_transport_config

def size_convert_utf8_to_base64(utf8_len):
    fill_byte_num = 0
    if utf8_len % 3 != 0:
        fill_byte_num = 1
    return (utf8_len // 3 + fill_byte_num) * 4


def size_convert_base64_to_utf8(base64_len):
    return (base64_len // 4) * 3


# 填充字符
filler = "$"
# 进行数据传输 数据包的尺寸(单位为字节 <base64编码>) 256的倍数
data_package_size = 1024

# 计算数据包长度
data_fixed_len = size_convert_base64_to_utf8(data_package_size)
# 记录填充符个数的字符串占用的长度
record_filler_len = len(str(data_fixed_len))
# 计算数据包传输是有效数据的最大字节数
valid_data_fixed_len = data_fixed_len - record_filler_len

客户端运行界面:
在这里插入图片描述
服务端运行界面:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值