私有协议上传需严格按服务端预期的字节流顺序和结构发送,关键在控制socket读写节奏、帧头网络字节序、分段收发、校验计算及断点续传机制。

私有协议上传的核心是控制 socket 读写节奏,不是套用 HTTP 库
直接拿 libcurl 或 Boost.Beast 发 HTTP 请求,解决不了私有协议文件上传——协议头格式、分包逻辑、校验方式、应答语义全由你定。关键不在“传”,而在“按对方服务端预期的字节流顺序和结构发”。常见错误是把文件整个 read() 进内存再 send(),结果服务端收不到包头或校验失败。
实操建议:
- 先用
tcpdump或Wireshark抓通正常客户端的流量,确认协议结构(比如前 4 字节是包长,第 5 字节是命令类型,后面是文件名长度+名字+文件内容) - 不要用
std::fstream::read()一次性读大文件;改用循环read()+send(),每次不超过 64KB,避免阻塞或内核缓冲区溢出 - 发送前必须按协议计算校验值(如
crc32或md5),并写入指定偏移,否则服务端直接断连
用 send() 分段发时必须检查返回值,send() 不保证一次发完
Linux/macOS 下 send() 可能只发出部分数据,Windows 的 send() 同样如此。返回值小于请求长度不是错误,而是常态。忽略这点会导致文件截断,且无任何异常抛出。
实操建议:
- 写一个健壮的
send_all(int sockfd, const void* buf, size_t len)函数,循环调用send()直到发完或出错 - 每次
send()后检查返回值:-1 表示错误(查errno是否为EINTR或EAGAIN),0 表示对端关闭,其他值表示实际发出字节数 - 若用非阻塞 socket,
send()返回 -1 且errno == EAGAIN时需等待可写事件(如用epoll_wait()或select())
文件内容不能裸发,必须封装进协议帧,且帧头要网络字节序
私有协议通常要求帧头含总长度、版本、命令 ID 等字段。如果直接把 uint32_t len = file_size 按本机序写进 buffer,x86 和 ARM 服务端会解析错——这是最隐蔽的兼容性坑。
实操建议:
- 所有多字节整数字段必须用
htonl()/htons()转网络序,接收方用ntohl()/ntohs()转回 - 帧结构建议用 POD struct 定义,但发送前需用
memcpy()拷贝字段,避免 struct padding 导致偏移错乱 - 例如协议定义:4 字节长度(网络序)+ 1 字节命令 + 1 字节保留 + N 字节文件名(含 '\0')+ 文件内容。构造时逐段 memcpy,别依赖
sizeof(MyHeader)
上传中断后怎么续传?私有协议得自己实现 offset 机制
HTTP 有 Range,FTP 有 REST,私有协议没有默认续传能力。服务端是否支持、如何标识断点、校验是否重算,全靠协议约定。很多团队上线后才发现不支持断点,只能重传整个 GB 级文件。
实操建议:
- 首次上传发全量帧;若服务端返回
"ACK_OFFSET:123456"类似应答,下次就从该 offset 开始发剩余内容 - 客户端本地保存已上传 offset 到临时文件(如
file.bin.uploaded),程序崩溃重启后先读这个值 - 续传帧头里必须带 offset 字段(同样网络序),服务端据此跳过已收部分并重新计算后续校验
协议越简单,越容易漏掉状态同步细节。比如没设计超时重试的 ack 机制,或没规定服务端对重复 offset 帧的处理方式,上传中途丢一个包就卡死。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











