TPC服务器断点续传代码该怎么写,有哪些关键步骤?

TPC服务器断点续传的核心写法是利用服务端和客户端各自记录已传输的字节偏移量,在连接恢复后通过HTTP Range头或Socket自定义协议通知对方从指定位置继续读写文件。如果直接重新传整个文件,浪费带宽不说,在弱网场景下根本传不完,下面从协议设计、核心代码、断点状态存储三个维度拆解,最后给出可直接落地的Java实现方案。

tpc服务器断点续传代码怎么写

先搞懂协议层如何支持断点续传

断点续传的根基在协议设计,如果你用的是HTTP协议,服务端必须支持Range头,行业共识认为,Range头是HTTP/1.1规范中定义的标准机制,服务端返回206 Partial Content状态码即可实现断点续传,客户端在重连时带上Range: bytes=1024-,服务端就从第1024字节开始读文件返回数据。

【java必会】文件分片、秒传、断点续传代码版
加载中
【java必会】文件分片、秒传、断点续传代码版
d
dragon7531
1.5万--
原视频地址

如果你们公司自己定TCP私有协议,那就必须在报文头里加一个字段来记录偏移量,我用过比较稳妥的方案是:自定义报文头包含magic number、command type、sequence id、offset、data length这几个字段,其中offset就是断点续传的关键,每次发送数据包时带上这个偏移量,接收方根据偏移量决定往文件的哪个位置写入。

Java实现服务端断点续传的核心代码

下面这套代码基于Netty框架实现,适用于TPC服务器文件上传场景,核心逻辑是记录每次请求的偏移量并支持从指定位置追加写入。

服务端核心处理逻辑:

public class FileUploadHandler extends SimpleChannelInboundHandler<FileUploadPacket> {
    // 存储文件上传状态,key为客户端标识+文件名,value为已上传字节数
    private ConcurrentHashMap<String, Long> uploadProgress = new ConcurrentHashMap<>();
    private String uploadDir = "/data/tpc_uploads/";
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FileUploadPacket packet) {
        String fileKey = packet.getClientId() + "_" + packet.getFileName();
        long offset = packet.getOffset();
        String filePath = uploadDir + packet.getFileName();
        try {
            // 打开RandomAccessFile,seek到指定偏移位置写入
            RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
            raf.seek(offset);
            raf.write(packet.getData());
            raf.close();
            // 更新进度
            uploadProgress.put(fileKey, offset + packet.getData().length);
            // 如果文件传完,做完整性校验
            if (packet.isLastPacket()) {
                String md5 = MD5Util.getFileMD5(filePath);
                if (md5.equals(packet.getFileMd5())) {
                    ctx.writeAndFlush(new UploadResponse(true, "文件上传完成"));
                } else {
                    ctx.writeAndFlush(new UploadResponse(false, "MD5校验失败,请重新上传"));
                }
                uploadProgress.remove(fileKey);
            } else {
                ctx.writeAndFlush(new UploadResponse(true, String.valueOf(offset + packet.getData().length)));
            }
        } catch (Exception e) {
            ctx.writeAndFlush(new UploadResponse(false, "写入失败: " + e.getMessage()));
        }
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        // 连接断开时保留uploadProgress中的记录,后续恢复时使用
        System.out.println("连接断开,保留当前断点状态");
    }
}

客户端断点续传代码:

public class FileUploadClient {
    private String serverHost = "192.168.10.20";
    private int serverPort = 8080;
    private String clientId = "client-001";
    private String baseDir = "/data/tpc_uploads/";
    public void upload(String filePath, String fileName) throws Exception {
        RandomAccessFile raf = new RandomAccessFile(filePath, "r");
        long fileLength = raf.length();
        // 查询服务端已经接收了多少字节
        long uploadedBytes = queryUploadedBytes(fileName);
        if (uploadedBytes >= fileLength) {
            System.out.println("文件已经上传过,无需重复上传");
            raf.close();
            return;
        }
        raf.seek(uploadedBytes);
        byte[] buffer = new byte[8192];
        int bytesRead;
        long offset = uploadedBytes;
        while ((bytesRead = raf.read(buffer)) != -1) {
            byte[] sendData = Arrays.copyOf(buffer, bytesRead);
            sendPacket(fileName, offset, sendData, false);
            offset += bytesRead;
            Thread.sleep(10); // 避免发送过快导致服务端处理不过来
        }
        // 发送最后一个包,标记文件结束
        sendPacket(fileName, offset, new byte[0], true);
        raf.close();
        System.out.println("文件上传完成");
    }
    private void sendPacket(String fileName, long offset, byte[] data, boolean last) {
        FileUploadPacket packet = new FileUploadPacket();
        packet.setClientId(clientId);
        packet.setFileName(fileName);
        packet.setOffset(offset);
        packet.setData(data);
        packet.setLastPacket(last);
        // 通过已有的channel发送
        Channel channel = getChannel();
        ChannelFuture future = channel.writeAndFlush(packet).syncUninterruptibly();
    }
}

断点状态存储选型

断点信息不能只放在内存里,应用重启就丢了,我建议区分场景:如果文件上传量不大,用Redis存储key-value对即可;大规模场景用MySQL或PostgreSQL的表来记录。

Redis存储方案:

# 存储客户端-文件的偏移量
SET tpc_upload_001_大型数据库备份.zip 52428800
# 设置过期时间,比如24小时
EXPIRE tpc_upload_001_大型数据库备份.zip 86400

数据库方案建表语句:

CREATE TABLE tpc_upload_progress (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    client_id VARCHAR(64) NOT NULL,
    file_name VARCHAR(255) NOT NULL,
    file_path VARCHAR(512) NOT NULL,
    uploaded_bytes BIGINT NOT NULL,
    total_bytes BIGINT NOT NULL,
    status TINYINT DEFAULT 0 COMMENT '0-上传中,1-已完成',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_client_file (client_id, file_name)
);

每次收到数据包就UPDATE tpc_upload_progress SET uploaded_bytes = ? WHERE client_id = ? AND file_name = ?,客户端重连后先查这张表,拿到已上传的字节数再继续传。

大文件断点续传的效率优化

多线程分块上传与断点续传结合

文件超过几个GB时,单线程传太慢了,业内专家指出,把文件切成多个块,每个块独立传输,单独记录断点,能大幅提升重传效率。

实现思路:把文件按固定大小切块(比如每块8MB),客户端为每块分配一个id,上传时带上块编号,服务端收到某个块后记录该块已完成,断线恢复后,只需重新传输未完成的块,已完成的块直接跳过,这样避免了从头传一遍的尴尬。

文件秒传逻辑的联想

断点续传往深了做,还能联动秒传功能,传输前先计算文件的MD5值,服务端查一下数据库里是否存在相同MD5的文件,如果存在,直接返回秒传成功,这能省掉大量不必要的传输流量。

据相关统计,在很多企业内部网络环境中,重复文件占比可达两成左右,秒传能明显降低带宽压力。

断点续传的性能瓶颈与调优

服务端写入磁盘时,RandomAccessFile的seek和write是线性操作的,高并发场景下性能会受限制,考虑引入队列,收到数据包后先放进内存队列,后台线程批量刷盘,但要注意刷盘策略,极端情况下掉电会丢失最后几秒的数据,对流式传输要求不高的场景,可以接受这个折中方案。

另一个调优点在于socket缓冲区,传输大文件时,把socket receive buffer调大一点,比如256KB,能减少网络拥塞导致的丢包和重传。

tpc客户端连接断开后如何续传

客户端自动重连与续传逻辑

代码里需要加一个重连机制,检测到连接断开后,使用指数退避策略重连,比如第一次1秒后重试,第二次2秒,第三次4秒,最大不超过30秒,重连成功后,调用查询进度的接口拿偏移量,继续传输。

public void uploadWithResume(String filePath, String fileName) throws Exception {
    int retryCount = 0;
    while (true) {
        try {
            connect();
            long uploaded = queryUploadedBytes(fileName);
            uploadFromOffset(filePath, fileName, uploaded);
            break;
        } catch (IOException e) {
            retryCount++;
            long waitTime = Math.min(30, (long) Math.pow(2, retryCount));
            System.out.println("连接断开," + waitTime + "秒后重试...");
            Thread.sleep(waitTime  1000);
            closeChannel();
        }
    }
}

续传时文件校验与防重复写入

续传前要做两件事:查询服务端已有字节数,再对比本地文件长度,如果本地文件的字节数比服务端记录的偏移量还小,说明本地文件也被修改过,需要重建断点,从头传,如果服务端偏移量大于本地文件长度,说明服务端数据有问题,需要删除服务端文件重新传。

还有种情况,客户端和服务端记录的偏移量一致,但服务端实际存储的数据是坏的,比如前一个连接传了一部分错误数据进去,此时需要服务端额外提供校验接口,用MD5比对已传部分的完整性,不校验的话,文件传完发现损坏,浪费时间事小,数据错误导致业务故障就麻烦了。

四种断点续传实现方式对比

实现方式 优点 缺点 适用场景
HTTP Range 标准协议,实现简单,各种工具支持 只适用于客户端主动拉取或上传场景 网页播放器、CDN分发、网盘上传
私有TCP + 偏移量字段 灵活控制,效率高 需要自己设计协议和状态管理 企业内部专用的传输工具
Ftp断点续传(REST命令) 传统FTP协议支持,运维熟悉 传输效率一般,吞吐量受限 传统制造业机房环境
基于对象存储的分片上传 依赖云服务商SDK,可靠性高 需引入云厂商依赖,有网络延迟开销 简米云OSS、酷番云COS等场景

tpc网关服务器断点续传常见问题排查

续传时进度丢失怎么办

优先检查Redis或数据库中的断点记录是否存在,Redis记录如果设了过期时间,比如24小时,超出时间后就没法续传了,需要调大过期时间,数据库记录如果写入失败,多半是并发写同一文件的记录产生了锁冲突,解决思路是给client_id + file_name设计唯一索引,用INSERT ... ON DUPLICATE KEY UPDATE做原子操作。

服务端返回偏移量不准确如何处理

这通常发生在多块数据包乱序到达时,比如第一个包还没写到磁盘,第二个包就把记录覆盖了,解决办法是保证同一文件写入的串行性,用ConcurrentHashMap按文件名加锁,确保同一时间只有一个线程在写同一个文件。

private ConcurrentHashMap<String, Object> fileLocks = new ConcurrentHashMap<>();
private Object getFileLock(String fileName) {
    return fileLocks.computeIfAbsent(fileName, k -> new Object());
}
public void writeToFile(String fileName, byte[] data, long offset) {
    Object lock = getFileLock(fileName);
    synchronized (lock) {
        try (RandomAccessFile raf = new RandomAccessFile(uploadDir + fileName, "rw")) {
            raf.seek(offset);
            raf.write(data);
        } catch (Exception e) {
            // 处理异常
        }
    }
}

断点续传对服务器磁盘空间有额外要求吗

有,断点续传会临时占用被传输文件大小约1.5倍的磁盘空间,例如要传100GB的文件,中途断了,已传的空间不会被释放,同时日志、临时文件和断点记录还需要额外空间,建议大文件上传目录预留至少文件大小两倍的空间,避免磁盘写满导致全部传输失败。

tpc服务器断点续传代码实现注意事项

处理网络异常时,记得区分临时抖动和永久失效,TCP长连接断开后,应该立刻停止当前线程的写操作,防止大量已发送但未被确认的数据堆积在socket缓冲区里,重连完成后,先检查双方约定的协议版本号是否一致,不一致则要升级客户端。

关于日志记录,每传输一个数据包都记录全量日志太占空间,按照经验,每传输满50MB记录一次日志即可,日志字段包括文件名、偏移量、时间戳、耗时。

代码发布时注意兼容性,服务端升级了断点续传协议,旧版本客户端还能不能连?如果协议不兼容,服务端要能识别旧客户端并返回升级提示。

tpc服务器断点续传代码常见问题解答

tpc服务器断点续传代码使用什么编程语言写最合适

Java和Go是目前最主流的选择,Java生态成熟,Netty在处理高并发网络IO方面表现稳定;Go语言自身标准库net包就能实现可靠的TCP服务,编译产物免部署,特别适合运维场景,选型依据是团队技术栈和业务规模,小规模传输用Python也可以。

断点续传期间服务端重启会导致已传数据丢失吗

如果断点状态存储与文件写入使用了内存映射或未刷盘的缓冲区,重启后数据必然丢失,稳妥的做法是每次写入成功就更新数据库记录并调用raf.getFD().sync()强制刷盘,虽然性能有点损失但数据安全,如果不要求强一致,每写一个块刷一次盘也是能接受的折中,重启后最多丢最后一个块的数据。

tp服务器文件上传断点续传会不会被防火墙阻断

TCP长连接在长时间无数据传输时,防火墙会断开空闲连接,解决方法是客户端定期发送心跳包,间隔建议30秒或60秒,如果服务端开启SO_KEEPALIVE,那只是操作系统层面的探测,不能完全替代自定义心跳,传输大文件期间,网络设备如果启用了超时限制,必须调整设备策略或缩短传输间隙。

首发原创文章,作者:王坚‌,如若转载,请注明出处:https://idctop.com/article/791461.html

赞 (0)
上一篇 2026年11月10日 03:24
下一篇 2026年11月10日 03:25

相关推荐

  • VPS被DDoS攻击了怎么办?,如何防御DDoS攻击?

    VPS 被 DDoS 攻击,最直接有效的办法是启用高防 IP 或 CDN 进行流量清洗,同时联系服务商解除黑洞,并从源头上限制不必要的端口和协议,VPS 被 DDoS 攻击怎么办?三步应急指南你正用 VPS 跑着业务,突然 SSH 连不上,网站打不开,服务商发来“流量异常”通知——大概率被 DDoS 了,别慌……

    2026年7月30日
    1800
  • 美国BageVmVPS测评,双ISP、原生IP实测体验,美国VPS哪家强,美国VPS推荐

    美国BageVmVPS凭借双ISP路由切换与原生IP优势,在2026年跨境业务场景中,依然是兼顾稳定性与SEO友好度的高性价比首选,尤其适合对网络质量有严苛要求的建站与开发用户,核心优势深度解析:双ISP与原生IP实战表现在2026年的VPS市场中,单纯的低价已无法吸引专业用户,网络质量成为决定业务成败的关键……

    2026年5月18日
    7300
  • 西子奥的斯21vf服务器如何操作?,按键操作步骤是什么?

    西子奥的斯21vf服务器怎么按?维修师傅的实用操作笔记西子奥的斯21vf服务器(型号通常为OTC-BM,又称LMCB板上的手持操作器)的核心操作逻辑其实很简单:通过服务器上的“菜单键(M)”、“确认键(SET)”和“上下翻页键(▲/▼)”组合调用电梯的调试、故障读取和参数修改功能,把这三个按键的逻辑理顺,大部分……

    2026年10月9日
    100
  • LiftUp英国是正品吗,LiftUp英国

    LiftUp英国作为专注高端留学背景提升与职业规划的平台,其核心优势在于提供基于英国本土教育体系的定制化项目,通过“学术科研+名企实习+竞赛认证”三位一体的服务,显著提升申请牛剑G5及罗素集团名校的成功率,适合预算充足、目标明确的高净值家庭及本科生群体,LiftUp英国核心服务架构与差异化优势在2026年的留学……

    2026年5月14日
    5700
  • 光遇PC一直显示服务器繁忙怎么办,是什么原因

    光遇pc一直显示服务器繁忙,最直接的解决方法是先检查本地网络连接,再重置游戏配置文件,如果问题依旧,大概率是官方服务器负载过高,建议更换网络节点或使用加速器,光遇pc一直显示服务器繁忙?从网络环境排查遇到服务器繁忙提示,首先别急着怀疑游戏,多数情况下问题出在本地网络,你可以先打开任务管理器,查看网络占用情况,如……

    2026年8月20日
    1300
  • 我的世界易ec服务器怎么加材质包?,具体步骤是什么?

    我的世界易ec服务器加材质包,核心就两条路:要么在客户端本地加载,要么把材质包传到服务器让全员自动下载,具体操作取决于你想自己看还是让全服统一,加材质包前必须搞懂的三个底层逻辑很多玩家一上来就拖文件,结果进服黑屏或材质失效,根子是没弄明白ec服务端和基岩版、Java版的资源包差异,下面这三件事是常见的坑,绕开它……

    2026年9月26日
    300
  • 物理机租用到底是什么意思,物理机租用多少钱一个月?

    物理机租用就是直接租用一台物理服务器的使用权,资源独享、性能稳定,适合对计算、存储和网络有高要求的业务场景,物理机租用到底是什么意思很多刚接触服务器的朋友会被各种概念绕晕:云服务器、虚拟主机、VPS、物理机……物理机租用就是你去服务商那里“包租”一整台实实在在的服务器,机箱、CPU、内存、硬盘、网卡全归你一个人……

    2026年7月29日
    1400
  • AIoT树根图片哪里找?高清AIoT树根素材下载

    AIoT树根图片不仅是技术架构的可视化呈现,更是工业互联网与智慧城市底层逻辑的解构图,其核心价值在于揭示了数据从物理世界向数字世界流动的“根系”脉络,直接决定了智能系统的稳定性与生长潜力,理解这一图像,等同于掌握了万物互联时代的底层密码,它将抽象的边缘计算、云端协同与数据处理具象化为一个有机的生命体结构,AIo……

    2026年3月20日
    10200
  • 香港BGP Standard月付9.99起好用吗?香港服务器适合做落地页吗

    AkileCloud香港BGP Standard套餐以月付¥9.99起的极低门槛,凭借原生BGP多线接入技术,成为追求高性价比与网络稳定性的用户解锁海外内容的首选方案,在当前的网络环境下,选择一款合适的海外服务器不仅要看价格,更要看线路质量,AkileCloud推出的香港BGP Standard套餐,正是为了解……

    2026年6月28日
    2800
  • 恒创科技服务器测评,大带宽实测表现,恒创科技服务器大带宽实测效果如何

    恒创科技服务器在大带宽实测中展现出极高的稳定性与低延迟特性,尤其适合高并发业务场景,其性价比在2026年国内独立服务器市场中处于第一梯队,带宽实测核心数据解析吞吐量与延迟表现在2026年Q1的行业基准测试中,恒创科技基于最新一代Intel Xeon Scalable处理器与NVMe SSD构建的节点,在10Gb……

    2026年5月17日
    5300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注