在这里插入图片描述

🔥草莓熊Lotso:个人主页

❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》

✨生活是默默的坚持,毅力是永久的享受!

🎬 博主简介:

在这里插入图片描述



前言:

很多 Linux 后端开发的新手,在学完 TCP Socket 基础 API 后,都能轻松写出一个 Echo 回显服务器,但一到真实业务场景就频频踩坑:想传输用户信息、计算请求等结构化数据,却不知道如何封装;客户端和服务端数据收发频繁出现解析错乱;明明 TCP 是可靠传输,却还是会出现 “粘包” 问题。这些问题的根源,在于只掌握了 Socket 的 API 调用,却没有理解应用层协议的核心价值,以及序列化与反序列化的底层逻辑。TCP 协议只负责字节流的可靠传输,却不关心字节流的业务含义,而应用层协议与序列化,正是我们在 TCP 之上构建业务能力的核心基石。本文将从 TCP IO 的底层本质出发,完整拆解应用层协议的设计逻辑、序列化与反序列化的核心原理,并基于 Jsoncpp 实现工业级的自定义协议,同时覆盖面试中 90% 的高频考点,让你不仅能写得出,更能懂底层、讲明白。


一. 重新理解 TCP IO 的底层真相(结合上面的前置知识图解来理解)

在正式进入协议设计之前,我们必须彻底搞懂read/write/recv/send这些 IO 系统调用的底层本质,这是理解网络通信的核心,也是面试的必考题。

在这里插入图片描述

1.1 TCP 全双工的底层实现:收发缓冲区

TCP 之所以支持全双工通信(同一个 socket 可以同时发送和接收数据),核心在于 Linux 内核为每个 TCP socket 都创建了两个独立的缓冲区:发送缓冲区 (Send Buffer) 和接收缓冲区 (Receive Buffer)

我们用文字描述完整的双工通信模型:

客户端应用层                          服务端应用层
      ↑↓                                   ↑↓
客户端read/write        网络        服务端read/write
      ↑↓                                   ↑↓
客户端内核态                          服务端内核态
[发送缓冲区] ←----------→ [接收缓冲区]
[接收缓冲区] ←----------→ [发送缓冲区]

这个模型带来了三个核心结论

  • 全双工的本质:发送和接收缓冲区相互独立,内核可以在向对端发送数据的同时,接收对端发来的数据,应用层可以在同一个 socket 上同时调用 read 和 write,不会产生冲突。
  • IO 的异步性:应用层调用 write 成功,仅仅代表数据被拷贝到了内核的发送缓冲区,不代表数据已经发送到了对端,更不代表对端已经收到了数据。数据何时发送、一次发多少、出错后如何重传,完全由内核的 TCP 协议栈自主控制,这也是 TCP 被称为 “传输控制协议” 的原因
  • 网络传输的本质:网络通信的全过程,本质上是发送方内核的发送缓冲区,通过网络将数据拷贝到接收方内核的接收缓冲区的过程

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.2 IO 系统调用的本质:内存拷贝

无论是write/send还是read/recv,这些 IO 系统调用的核心动作只有一个:内存拷贝

发送端:write/send 的执行流程

  • 应用层调用write(sockfd, buffer, len),传入用户空间的缓冲区地址和长度。
  • 内核将用户空间 buffer 中的数据,拷贝到该 socket 对应的内核发送缓冲区中
  • 拷贝完成后,write 函数立即返回,后续由 TCP 协议栈根据滑动窗口、拥塞控制机制,将发送缓冲区中的数据封装成 TCP 报文,发送到网络中。

接收端:read/recv 的执行流程

  • 内核通过网卡收到对端发来的 TCP 报文(怎么知道网卡有东西的,这个就是网卡触发了硬件中断),校验无误后,将数据放入该 socket 对应的内核接收缓冲区中。
  • 应用层调用read(sockfd, buffer, len),内核将接收缓冲区中的数据,拷贝到用户空间的 buffer 中
  • 拷贝完成后,read 函数返回实际读取到的字节数,应用层即可处理收到的数据。

这里有一个面试高频考点:调用 write 返回成功,就代表对方收到数据了吗?

答案是否定的。write 成功仅代表数据被成功拷贝到了内核发送缓冲区,此时如果服务器宕机,数据依然会丢失。TCP 协议栈会保证数据可靠地发送到对端,但应用层无法通过 write 的返回值感知这一点。

1.3 内核视角:TCP 报文的生命周期

从内核的视角来看,所有的网络报文都会被封装成一个核心结构体:struct sk_buff(socket buffer),这是 Linux 内核网络协议栈的核心数据结构。

内核处理报文的完整流程:

  • 网卡收到物理网络中的数据后,触发硬件中断,内核将数据拷贝到内核内存中,构建sk_buff结构体,描述报文的完整信息。
  • 内核通过链路层、网络层、传输层的逐层解析,确认报文对应的 socket,将sk_buff放入该 socket 的 接收队列sk_receive_queue 中。
  • 应用层调用 read 时,内核从接收队列中取出sk_buff,将数据拷贝到用户空间,完成读取操作。
  • 应用层调用 write 时,内核将用户数据拷贝到发送缓冲区,构建sk_buff结构体,放入该 socket 的 发送队列sk_write_queue 中,由协议栈负责发送。

sk_buff的设计极其精妙,它通过head/data/tail/end四个指针,实现了协议头的快速封装与解包:添加协议头时,只需向前移动data指针;解包时,只需向后移动data指针,无需频繁的内存拷贝,这也是 Linux 网络协议栈高性能的核心原因。

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


二. 为什么我们需要应用层自定义协议?

2.1 再谈协议的本质

  • 协议,本质上是通信双方的约定与规范

  • Socket 的读写 API,在底层都是以字节流 / 字符串的形式收发数据。如果我们只需要传输简单的回显字符串,原生 API 完全够用;但在真实业务中,我们需要传输的往往是有明确业务含义的结构化数据:比如网络计算器需要传输两个操作数和运算符、聊天软件需要传输发送人昵称、消息内容、发送时间、用户头像等信息。

  • 如何让发送方的结构化数据,能被接收方准确、无歧义地解析?这就需要双方提前约定好数据的格式、字段含义、边界规则,而这套约定,就是应用层协议。

举个最简单的例子:我们要实现一个网络版加法器,客户端向服务端发送两个数字,服务端计算后返回结果。这里就有两种最基础的协议约定方案:

  • 方案一:简单字符串约定
    • 客户端固定发送形如"1+2"的字符串,约定:字符串包含两个整形操作数,中间有且仅有一个运算符,数字与运算符之间无空格。服务端收到后按规则拆分字符串,提取数字和运算符进行计算。
  • 方案二:结构化数据约定
    • 定义结构体表示交互信息,发送方将结构体按照固定规则转换成字符串(序列化),接收方收到后按照相同规则还原成结构体(反序列化)。

方案一虽然简单,但扩展性极差,一旦业务需要新增字段、处理复杂逻辑,字符串的拆分与解析会变得极其繁琐,还极易出错;而方案二正是工业级开发的标准做法,也是本文的核心讲解内容。

在这里插入图片描述

2.2 TCP 面向字节流的特性带来的核心痛点

TCP 是面向字节流的传输协议,这是它最核心的特性,也是绝大多数新手踩坑的根源。

面向字节流的核心含义是:TCP 不关心应用层传输的数据是什么格式、有没有业务边界,它只负责把发送方写入的字节流,可靠、有序地传递给接收方,但不保证接收方的 read 调用次数和发送方的 write 调用次数一一匹配

举个例子:

  • 客户端分两次调用 write,分别写入"1+2""3*4"两个请求
  • 服务端调用 read 时,可能一次性读到"1+23*4",两个请求粘在了一起(粘包问题)
  • 也可能第一次读到"1+",第二次读到"23*4",一个请求被拆分成了两次读取(半包问题)

TCP 本身不会帮我们处理业务报文的边界,这个问题必须由应用层协议来解决。

2.3 结构化数据的跨平台 / 跨语言传输痛点

很多新手会问:我直接把结构体通过 socket 发送出去不行吗?为什么还要序列化?

直接发送结构体,在极其受限的场景下(客户端和服务端都是同平台、同语言、同编译器、同编译选项)可以运行,但在工业级开发中,这是绝对不推荐的做法,核心问题有两个:

  • 平台字节对齐差异:不同平台、不同编译器对结构体的内存对齐规则不同,同样的结构体在 32 位和 64 位系统下,占用的内存大小可能完全不同,会直接导致数据解析错乱。
  • 跨语言兼容性为零:结构体是 C/C++ 的语法特性,如果服务端用 C++ 开发,客户端用 Java/Python/Go 开发,对方根本无法识别 C++ 的结构体内存布局,完全无法通信。

而序列化,正是解决这个问题的核心方案:无论是什么语言、什么平台,都能将结构化数据转换成统一格式的字节流 / 字符串,接收方再按照统一规则还原成本地的结构化数据,实现跨平台、跨语言的网络通信。

在这里插入图片描述


三. 序列化与反序列化:网络传输的核心基石

3.1 核心定义

  • 序列化:将内存中的结构化数据(结构体 / 类对象),按照固定规则转换成连续的字节流 / 字符串的过程,核心目的是方便网络传输与持久化存储
  • 反序列化:序列化的逆过程,将网络中收到的字节流 / 字符串,按照相同的规则还原成内存中的结构化数据,核心目的是方便上层业务逻辑处理

简单来说,序列化就是 “多变一”,把分散的多个字段打包成一个可传输的整体;反序列化就是“一变多”,把收到的整体数据还原成可操作的多个字段。

在这里插入图片描述
在这里插入图片描述

3.2 主流序列化方案对比

在工业级开发中,我们不会手动实现序列化逻辑,而是使用成熟的开源方案,不同方案适用于不同的业务场景:

序列化方案 核心特点 优点 缺点 适用场景
Json 文本格式,键值对结构 可读性极强、跨语言全兼容、使用简单、无需预编译 序列化后体积较大、性能一般 Web API、配置文件、轻量级网络通信
Protobuf 二进制格式,Google 开源 序列化后体积极小、性能极高、支持版本兼容 可读性差、需要预编译 proto 文件 微服务 RPC、高性能后端通信、移动端网络通信
XML 文本格式,标签结构 规范性强、支持复杂嵌套结构 体积臃肿、解析性能差 传统配置文件、部分老系统接口
自定义二进制 手动定制二进制格式 极致的性能与体积控制 开发成本高、兼容性差、无跨语言能力 嵌入式设备、极致性能要求的底层通信

本文我们采用Jsoncpp库实现序列化与反序列化,它是 C++ 中最常用的 Json 处理库,API 简单易用,完全满足绝大多数后端业务场景的需求。

在这里插入图片描述
在这里插入图片描述


四. 实战落地:自定义协议完整实现

我们以网络版计算器为业务场景,完整实现一套工业级的应用层协议,包含:协议设计、序列化 / 反序列化、粘包问题解决三大核心模块。
在这里插入图片描述

4.1 环境准备

Jsoncpp 库的安装非常简单,在 Ubuntu/Debian 系统下执行:

sudo apt-get install -y libjsoncpp-dev

CentOS/RHEL 系统下执行:

sudo yum install -y jsoncpp-devel

编译时需要链接 jsoncpp 库,编译指令需加上-ljsoncpp参数。

在这里插入图片描述

4.2 协议设计:请求与应答报文

协议设计的第一步,是定义通信双方的结构化报文,我们分为请求报文应答报文两类。

请求报文(Client → Server)
客户端向服务端发送的计算请求,包含三个核心字段:

字段名 类型 含义
datax int 第一个操作数
datay int 第二个操作数
oper char 运算符,支持+ - * / %

应答报文(Server → Client)
服务端向客户端返回的计算结果,包含两个核心字段:

字段名 类型 含义
result int 计算结果,仅当 exitcode 为 0 时有效
exitcode int 状态码,0 表示计算成功,非 0 表示错误码(如除零错误、非法运算符)

这套报文结构,就是我们自定义协议的核心,客户端和服务端都必须遵循这套规范进行数据的序列化与反序列化。

4.3 基于 Jsoncpp 的序列化与反序列化实现

我们将协议的核心实现封装在Protocol.hpp头文件中,客户端和服务端只需引入该头文件,即可使用统一的协议规范,这也是协议开发的最佳实践。

完整协议头文件实现

#ifndef __PROTOCOL__HPP
#define __PROTOCOL__HPP

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>

// 协议分隔符约定
const std::string LineBreakSep = "\r\n";

// ===================== 请求报文:client -> server =====================
class Request
{
public:
    // 构造函数
    Request() : _data_x(0), _data_y(0), _oper(0) {}
    Request(int x, int y, char op) : _data_x(x), _data_y(y), _oper(op) {}

    /**
     * @brief 序列化:将结构化的请求对象,转换成Json字符串
     * @param out 输出参数,存储序列化后的字符串
     * @return 序列化是否成功
     */
    bool Serialize(std::string *out)
    {
        Json::Value root;
        // 将结构化字段填入Json对象
        root["datax"] = _data_x;
        root["datay"] = _data_y;
        root["oper"] = _oper;
        
        // 使用FastWriter生成无格式的紧凑Json字符串,减少传输体积
        Json::FastWriter writer;
        *out = writer.write(root);
        return true;
    }

    /**
     * @brief 反序列化:将Json字符串,还原成结构化的请求对象
     * @param in 输入参数,收到的Json字符串
     * @return 反序列化是否成功
     */
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        // 解析Json字符串
        bool parse_success = reader.parse(in, root);
        if(!parse_success)
        {
            return false;
        }
        // 从Json对象中提取字段,还原结构化数据
        _data_x = root["datax"].asInt();
        _data_y = root["datay"].asInt();
        _oper = root["oper"].asInt();
        return true;
    }

    // 字段获取接口
    int GetX() const { return _data_x; }
    int GetY() const { return _data_y; }
    char GetOper() const { return _oper; }

    // 调试打印接口
    void DebugPrint()
    {
        std::cout << "Request Debug:" << std::endl;
        std::cout << "datax: " << _data_x << std::endl;
        std::cout << "datay: " << _data_y << std::endl;
        std::cout << "oper: " << _oper << std::endl;
    }

    ~Request() = default;

private:
    int _data_x;    // 第一个操作数
    int _data_y;    // 第二个操作数
    char _oper;     // 运算符 + - * / %
};

// ===================== 应答报文:server -> client =====================
class Response
{
public:
    // 构造函数
    Response() : _result(0), _exitcode(0) {}
    Response(int result, int code) : _result(result), _exitcode(code) {}

    /**
     * @brief 序列化:将结构化的应答对象,转换成Json字符串
     * @param out 输出参数,存储序列化后的字符串
     * @return 序列化是否成功
     */
    bool Serialize(std::string *out)
    {
        Json::Value root;
        root["result"] = _result;
        root["code"] = _exitcode;
        
        Json::FastWriter writer;
        *out = writer.write(root);
        return true;
    }

    /**
     * @brief 反序列化:将Json字符串,还原成结构化的应答对象
     * @param in 输入参数,收到的Json字符串
     * @return 反序列化是否成功
     */
    bool Deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool parse_success = reader.parse(in, root);
        if(!parse_success)
        {
            return false;
        }
        _result = root["result"].asInt();
        _exitcode = root["code"].asInt();
        return true;
    }

    // 字段设置与获取接口
    void SetResult(int res) { _result = res; }
    void SetCode(int code) { _exitcode = code; }
    int GetResult() const { return _result; }
    int GetCode() const { return _exitcode; }

    // 调试打印接口
    void DebugPrint()
    {
        std::cout << "Response Debug:" << std::endl;
        std::cout << "result: " << _result << std::endl;
        std::cout << "exitcode: " << _exitcode << std::endl;
    }

    ~Response() = default;

private:
    int _result;    // 计算结果
    int _exitcode;  // 状态码:0成功,非0错误
};

#endif

源码核心解读

  • Json::Value 万能对象:Jsoncpp 的核心类,用于存储 Json 的键值对结构,支持 int、string、数组、嵌套对象等所有 Json 数据类型,我们通过root["key"] = value的方式填充字段。

  • 序列化核心逻辑:通过Json::FastWriterJson::Value对象转换成紧凑的字符串,相比StyledWriter,它不会添加额外的空格和换行,能有效减少网络传输的数据量。

  • 反序列化核心逻辑:通过Json::Reader解析收到的 Json 字符串,还原成Json::Value对象,再通过asInt()等方法提取对应类型的字段,还原成结构化数据。

  • 接口封装:所有成员变量都设为 private,通过 public 接口访问,保证了数据的封装性,避免业务代码直接修改字段导致协议错乱。

  • 下面的图中补充了一下JSONCPP的使用,比如StreamWriterBulider,更详细的补充见飞书笔记

在这里插入图片描述
在这里插入图片描述

4.4 解决粘包问题:报文编解码方案(补充)

序列化解决了结构化数据的传输问题,但还没有解决 TCP 面向字节流带来的粘包 / 半包问题。我们采用 “报文长度 + 分隔符” 的经典方案,实现报文的编解码,彻底解决粘包问题。

我们约定最终的网络传输报文格式为:

[报文长度]\r\n[序列化后的业务报文]\r\n

例如:序列化后的业务报文长度为 30,那么最终发送的报文为"30\r\n{"datax":10,"datay":20,"oper":43}\r\n"

编解码核心实现

我们在Protocol.hpp中补充编解码函数:

/**
 * @brief 编码函数:给业务报文添加长度头部和分隔符,封装成完整的网络传输报文
 * @param message 输入参数,序列化后的业务报文
 * @return 封装后的完整网络报文
 */
std::string Encode(const std::string &message)
{
    // 1. 将报文长度转为字符串
    std::string len_str = std::to_string(message.size());
    // 2. 按照约定格式封装报文
    std::string package = len_str + LineBreakSep + message + LineBreakSep;
    return package;
}

/**
 * @brief 解码函数:从接收缓冲区中提取完整的业务报文,处理粘包/半包
 * @param package 输入输出参数,当前的接收缓冲区数据
 * @param message 输出参数,提取出的完整业务报文
 * @return 是否提取到了完整的报文
 */
bool Decode(std::string &package, std::string *message)
{
    // 1. 查找第一个分隔符,提取报文长度
    auto pos = package.find(LineBreakSep);
    if (pos == std::string::npos)
    {
        // 没找到分隔符,说明报文不完整,返回false
        return false;
    }

    // 2. 提取报文长度字符串,转为整型
    std::string len_str = package.substr(0, pos);
    int message_len = std::stoi(len_str);

    // 3. 计算完整报文的总长度
    // 总长度 = 长度字符串长度 + 2个分隔符长度 + 业务报文长度
    int total_package_len = len_str.size() + message_len + 2 * LineBreakSep.size();

    // 4. 判断缓冲区中是否有完整的报文
    if (package.size() < total_package_len)
    {
        // 缓冲区数据不足,报文不完整,返回false
        return false;
    }

    // 5. 提取完整的业务报文
    *message = package.substr(pos + LineBreakSep.size(), message_len);

    // 6. 从缓冲区中移除已经处理过的报文,保留剩余数据
    package.erase(0, total_package_len);

    return true;
}

编解码逻辑核心解读

  • 编码逻辑:在业务报文前添加长度字段,并用\r\n作为分隔符,让接收方可以明确知道业务报文的准确长度,从而判断报文是否完整。
  • 解码逻辑的核心设计
    • 半包处理:如果缓冲区中没有找到分隔符,或者数据长度不足一个完整报文,直接返回 false,不做任何处理,等待下次读取更多数据后再解析。
    • 粘包处理:提取完一个完整报文后,只移除缓冲区中已处理的部分,剩余的数据会保留在缓冲区中,等待下一次解码,不会丢失粘在一起的后续报文。
    • 原子性处理:只有确认缓冲区中有完整报文时,才会提取数据,否则不修改缓冲区,保证了解析的安全性。

这套编解码方案,是工业级网络开发中处理粘包问题的标准方案,HTTP、RPC 等协议的底层,都是基于类似的 “长度 + 分隔符” 设计。

4.5 协议功能测试

我们编写简单的测试代码,验证序列化、反序列化、编解码的完整流程:

#include "Protocol.hpp"

int main()
{
    // 1. 创建请求对象
    Request req(10, 20, '+');
    std::cout << "===== 原始请求 =====" << std::endl;
    req.DebugPrint();

    // 2. 序列化请求
    std::string serialize_str;
    req.Serialize(&serialize_str);
    std::cout << "\n===== 序列化后的Json字符串 =====" << std::endl;
    std::cout << serialize_str;

    // 3. 编码成网络传输报文
    std::string send_package = Encode(serialize_str);
    std::cout << "\n===== 编码后的网络报文 =====" << std::endl;
    std::cout << send_package;

    // 模拟网络传输:接收缓冲区收到数据
    std::string recv_buffer = send_package;
    std::string message;

    // 4. 解码提取业务报文
    bool decode_success = Decode(recv_buffer, &message);
    if(decode_success)
    {
        std::cout << "\n===== 解码后的业务报文 =====" << std::endl;
        std::cout << message;

        // 5. 反序列化还原请求对象
        Request recv_req;
        recv_req.Deserialize(message);
        std::cout << "\n===== 反序列化后的请求 =====" << std::endl;
        recv_req.DebugPrint();
    }

    return 0;
}

编译运行后,我们可以看到完整的流程执行成功,结构化数据经过序列化、编码、网络传输、解码、反序列化后,被完整还原,完美解决了结构化数据传输和粘包问题。


五. 面试高频核心考点总结

  • 什么是粘包问题?为什么会出现?怎么解决?

    • 粘包问题:TCP 面向字节流的特性,导致多个业务报文粘在一起,或者一个业务报文被拆分,接收方无法准确解析出完整的业务报文。
    • 出现原因:TCP 不关心应用层的业务边界,只负责字节流的可靠传输;应用层多次 write 的数据,可能被内核合并成一个 TCP 报文发送;内核收到的多个 TCP 报文,可能被应用层一次 read 全部读取。
    • 解决方案:应用层自定义协议,通过报文长度 + 分隔符、固定长度报文、特殊结束符等方式,明确业务报文的边界,其中 “报文长度 + 分隔符” 是工业级标准方案。
  • 调用 write/send 返回成功,代表数据已经发送到对端了吗?为什么?

    • 不代表。write/send 调用成功,仅代表数据已经从用户空间拷贝到了内核的 socket 发送缓冲区,不代表数据已经发送到网络,更不代表对端已经收到。数据的实际发送由内核的 TCP 协议栈控制,只有收到对端的 ACK 应答,才能确认数据被对端成功接收。
  • TCP 为什么支持全双工通信?底层实现是什么?

    • TCP 全双工的底层实现,是内核为每个 TCP socket 分配了两个完全独立的发送缓冲区接收缓冲区。发送和接收操作互不干扰,内核可以同时处理数据的发送和接收,应用层可以在同一个 socket 上同时进行读写操作,因此 TCP 支持全双工通信。
  • 序列化与反序列化的核心作用是什么?为什么不能直接发送结构体?

    • 核心作用:将内存中的结构化数据转换成可在网络中传输的字节流,实现跨平台、跨语言的数据交换,同时保证接收方可以准确还原结构化数据。
    • 不能直接发送结构体的原因:① 不同平台、不同编译器的结构体内存对齐规则不同,会导致数据解析错乱;② 结构体是语言相关的,无法实现跨语言通信;③ 结构体中如果包含指针类型,直接发送只会传递指针地址,对端无法访问对应的数据。
  • TCP 已经是可靠传输协议了,为什么还需要应用层协议?

    • TCP 的可靠,仅保证字节流能有序、无差错、不重复地从发送方传递到接收方,但它不理解字节流的业务含义,不处理业务报文的边界,也不保证应用层报文的完整性。
    • 应用层协议的核心作用,是定义业务数据的格式、边界、交互规则,让通信双方能准确解析出有业务含义的数据,实现具体的业务逻辑。

结尾:

🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!

结语:应用层自定义协议与序列化,是从 TCP Socket 入门到工业级网络开发的必经之路。我们日常使用的 HTTP、HTTPS、RPC、WebSocket 等所有应用层协议,其底层核心逻辑都离不开本文讲解的:结构化报文约定、序列化与反序列化、报文边界处理三大核心模块。理解了本文的内容,你不仅能解决网络开发中的粘包、数据解析等常见问题,更能读懂各类应用层协议的设计思路,为后续学习高性能网络框架、分布式系统、微服务开发打下最坚实的基础。

✨把这些内容吃透超牛的!放松下吧✨
ʕ˘ᴥ˘ʔ
づきらど

在这里插入图片描述

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐