当前位置: 首页 > news >正文

linux——自定义协议

什么是自定义协议?

自定义协议 是指根据应用程序的需求,开发者定义的一种数据格式或传输规则。在网络通信中,协议用于描述双方通信时的数据结构、数据格式、传输顺序等。

  • 定义:自定义协议是开发者为特定应用或场景设计的协议,用来满足应用之间的数据交换需求。与标准协议(如HTTP、TCP/IP)不同,自定义协议不依赖于任何标准,而是根据具体需求自行设计。

  • 用途例如,在一个客户端与服务器之间进行通信时,客户端发送特定格式的数据请求,服务器根据预定义的协议格式解析并返回响应数据。自定义协议的设计通常需要考虑数据的序列化、反序列化、传输效率和安全性。

  • 示例:比如,设计一个简单的数学计算协议,客户端发送请求(比如两个数字和一个运算符),服务器进行处理,返回结果。自定义协议在此场景中的格式可以是:数字1 运算符 数字2

    // 请求结构
    struct Request {int num1;int num2;char op; // 操作符(如 +,-,*,/)
    };// 响应结构
    struct Response {int result;int code; // 0: 成功,1: 错误
    };
    

    通信双方根据这个格式进行数据的传递和解析,即构成了自定义协议.

自定义协议 是属于 应用层 的概念。

  • 应用层 是 OSI 模型中的最上层,也是计算机网络协议栈中最接近用户的层。它定义了应用程序之间如何交换数据,涵盖了各种应用协议,如 HTTP、FTP、SMTP,以及你提到的自定义协议。

  • 自定义协议是应用层的一部分,通常由应用程序开发者定义,用于特定应用的需求。自定义协议可以定义数据格式、通信规则、如何打包和解包数据等。它通常使用其他协议(如 TCP/IP、UDP)来实现数据的传输。

说到自定义协议,我们还要谈到序列化和反序列化这两个概念。

序列化与反序列化简介

序列化反序列化 是计算机程序在处理数据传输和存储时常用的两个概念它们用于将数据从一种格式转化为另一种格式通常在网络通信、文件存储和跨平台数据交换中广泛应用。

1. 序列化(Serialization)

序列化 是将程序中的对象、数据结构或内存中的数据转换为可存储或传输的格式的过程。常见的格式有 JSON、XML、二进制数据流等。

  • 目的将内存中的复杂数据结构转换为字节流或其他格式,使其能够被存储在文件、数据库中或通过网络传输。

  • 应用场景

    • 网络通信:将对象或数据结构转化为字节流或字符串,在客户端与服务器之间传输。

    • 数据持久化:将对象序列化为文件或数据库记录,便于后续读取。

    • 跨平台通信:将数据转换为跨平台通用格式,支持不同系统之间的通信。

2. 反序列化(Deserialization)

反序列化将序列化后的数据(如字节流或字符串)转换回程序能够理解的对象、数据结构或实体的过程。

  • 目的:将传输或存储的字节流转换回原始数据结构,使程序可以对其进行操作。

  • 应用场景

    • 网络通信:将接收到的数据字节流还原为原始对象,供程序进一步处理。

    • 数据恢复:从存储中恢复对象数据,供程序重新使用。

将数据转化为字节流而不是直接传输结构体本身,主要是出于以下几个原因:

  • 跨平台兼容性:不同平台的内存布局差异会导致直接传输结构体时的兼容性问题。

  • 灵活性:序列化为字节流或标准化格式,可以提高程序的灵活性、可扩展性,并支持版本控制。

  • 网络传输效率:通过序列化,你可以优化数据的大小,减少带宽浪费,并更容易适应不同的网络协议。

  • 调试与可读性:序列化格式(如 JSON)更加易于调试和查看,对于错误处理和日志记录也更为方便。

  • 安全性:序列化后的数据更容易进行验证,保证数据的完整性和安全性。

TCP的全双工通信总结

全双工通信 是指通信双方可以同时进行数据的发送和接收。在 TCP(传输控制协议)中,全双工通信 是其核心特性之一,支持客户端和服务器之间在同一个连接上同时发送和接收数据。

TCP全双工通信的关键要点:

  1. 独立的发送与接收缓冲区

    • 每一端(客户端和服务器)都有两个独立的缓冲区:一个用于发送数据(发送缓冲区),另一个用于接收数据(接收缓冲区)。

    • 发送和接收操作互不干扰,可以同时进行。

  2. 同时发送和接收数据

    • 在一个TCP连接上,客户端和服务器可以同时发送和接收数据。例如,客户端可以在发送请求的同时接收服务器的响应,服务器也可以在接收客户端请求时,立即返回数据。

  3. 数据流双向传输

    • TCP连接中的数据流是双向的。每个方向上都可以独立进行数据传输,而不会相互阻塞。

  4. 基于缓冲区的管理

    • 发送数据时,数据从应用层的缓冲区拷贝到 发送缓冲区,然后通过网络传输给接收方。

    • 接收方的数据从网络传输到接收缓冲区,应用层可以从接收缓冲区读取数据。

    • 这两个缓冲区(发送缓冲区和接收缓冲区)的独立性保证了全双工通信的流畅性。

  5. 流量控制与拥塞控制

    • TCP采用 流量控制拥塞控制,确保在通信过程中,双方的发送速率匹配,避免数据丢失或网络拥塞。

  6. 阻塞I/O操作

    • TCP的I/O函数(如 readwrite)通常是阻塞的,确保数据的同步处理。发送和接收操作在每个缓冲区内独立进行,但为了避免数据冲突或丢失,它们通过阻塞机制进行同步。

网络版计算器

例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端.

约定方案一:

客户端发送一个形如"1+1"的字符串;

这个字符串中有两个操作数, 都是整形;

两个数字之间会有一个字符是运算符, 运算符只能是 + ;数字和运算符之间没有空格;

约定方案二:

定义结构体来表示我们需要交互的信息;

发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;这个过程叫做 "序列化" 和“反序列化”。

1.Socket.hpp
#pragma once#include <iostream>
#include <string>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "log.hpp"const int backlog =5;
extern Log lg;enum
{SOCKERR = 1,BINDERR = 2,LISTENERR
};class Sock
{
public:Sock(){}~Sock(){}public:void Socket(){sockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ < 0){lg("fatal", "socket create fail errno:%d strerrno:%s", errno, strerror(errno));exit(1);}}void Bind(uint16_t port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr=INADDR_ANY;socklen_t len = sizeof(local);if (bind(sockfd_, (struct sockaddr *)&local, len) < 0){lg("fatal", "bind fail errno:%d,strerror:%s\n", errno, strerror(errno));exit(BINDERR);}}void Listen() // 变为监听窗口,错了就别玩了{if (listen(sockfd_, backlog) < 0){lg("fatal", "Listen fail errno:%d,strerror:%s\n", errno, strerror(errno));exit(LISTENERR);}}int Accpect(string*ip,uint16_t* port)//输出型参数,谁链接的我,对方的端口,ip{struct sockaddr_in temp;socklen_t len=sizeof(temp);int newfd=accept(sockfd_,(struct sockaddr*)&temp,&len);if(newfd<0){lg("fatal", "accpect fail errno:%d,strerror:%s\n", errno, strerror(errno));return -1;}*port=ntohs(temp.sin_port);string ipstr=inet_ntoa(temp.sin_addr);*ip=ipstr;return newfd;}bool Connect(string& ip,uint16_t port)//服务器的端口,ip{struct sockaddr_in server;bzero(&server,0);server.sin_family=AF_INET;server.sin_port=htons(port);server.sin_addr.s_addr=inet_addr(ip.c_str());int ret=connect(sockfd_,(struct sockaddr*)&server,sizeof(server));if(ret<0){lg("info", "Connect fail errno:%d,strerror:%s\n", errno, strerror(errno));return false;}return true;}void Close(){close(sockfd_);}
public:int sockfd_;
};
2.Protocal.hpp
#pragma once#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>// #define Myself 1   //编译的时候可以 用-DMyselfusing namespace std;const string blank_sep = " ";
const string protocal_sep = "\n";//"x op y" ->
//"len"/n"x op y"/n
std::string Encode(string &content)
{int len = content.size();string package = to_string(len);package += protocal_sep;package += content;package += protocal_sep;return package;
}//"len"/n"x op y"/n ->  解析可能错误
//"x op y"
bool Decode(string &package, string *content)
{int pos = package.find(protocal_sep);if (pos == string::npos)return false;string len_str = package.substr(0, pos);int len = stoi(len_str);int to_len = len_str.size() + 2 + len;if (package.size() < to_len)return false;*content = package.substr(pos + 1, len); // 位置 加 长度package.erase(0, to_len); // 解析的这一段,从缓冲区里去掉return true;
}class Request
{
public:Request(int x, int y, char op) : x_(x), y_(y), op_(op){}Request(){}~Request(){}public:bool Serialize(string *content) // 序列化为 字节流{
#ifdef Myselfstring s = to_string(x_);s += blank_sep;s += op_;s += blank_sep;s += to_string(y_);*content = s;return true;
#elseJson::Value root;root["x"] = x_;root["y"] = y_;root["op"] = op_;Json::FastWriter w;*content = w.write(root);return true;
#endif}bool Deserialize(string &in) // 反序列化 将字节流解析{
#ifdef Myselfint l = in.find(blank_sep);if (l == string::npos)return false;string x = in.substr(0, l); // 左闭,右开int r = in.rfind(blank_sep);if (r == string::npos)return false;string y = in.substr(r + 1);if (l + 2 != r)return false;x_ = stoi(x);y_ = stoi(y);op_ = in[l + 1];return true;
#elseJson::Reader R;Json::Value root;bool ret = R.parse(in.c_str(), root);if (!ret)return false;x_ = root["x"].asInt();y_ = root["y"].asInt();op_ = root["op"].asInt();return true;#endif}void Printf(){cout << "任务请求" << x_ << op_ << y_ << "=?" << endl;}public:int x_;int y_;char op_;
};class Respone
{
public:Respone(int res, int c) : result_(res), code_(c){}Respone(){}~Respone(){}public:bool Serialize(string *out) // 序列化 形成字节流{
#ifdef Myselfstring s = to_string(result_);s += blank_sep;s += to_string(code_);*out = s;return true;
#elseJson::Value root;root["result"] = result_;root["code"] = code_;Json::FastWriter w;*out = w.write(root);return true;
#endif}bool Deserialize(string &in) // 反序列化 字节流解析{
#ifdef Myselfint pos = in.find(blank_sep);if (pos == string::npos)return false;string x = in.substr(0, pos);int result = stoi(x);string y = in.substr(pos + 1);int code = stoi(y);result_ = result;code_ = code;return true;
#elseJson::Reader R;Json::Value root;bool ret = R.parse(in.c_str(), root);if (!ret)return false;result_=root["result"].asInt();code_=root["code"].asInt();return true;
#endif}void Printf(){cout << "result " << result_ << "  code " << code_ << endl;}public:int result_;int code_;
};
3.Server.cal
#pragma once#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>// #define Myself 1   //编译的时候可以 用-DMyselfusing namespace std;const string blank_sep = " ";
const string protocal_sep = "\n";//"x op y" ->
//"len"/n"x op y"/n
std::string Encode(string &content)
{int len = content.size();string package = to_string(len);package += protocal_sep;package += content;package += protocal_sep;return package;
}//"len"/n"x op y"/n ->  解析可能错误
//"x op y"
bool Decode(string &package, string *content)
{int pos = package.find(protocal_sep);if (pos == string::npos)return false;string len_str = package.substr(0, pos);int len = stoi(len_str);int to_len = len_str.size() + 2 + len;if (package.size() < to_len)return false;*content = package.substr(pos + 1, len); // 位置 加 长度package.erase(0, to_len); // 解析的这一段,从缓冲区里去掉return true;
}class Request
{
public:Request(int x, int y, char op) : x_(x), y_(y), op_(op){}Request(){}~Request(){}public:bool Serialize(string *content) // 序列化为 字节流{
#ifdef Myselfstring s = to_string(x_);s += blank_sep;s += op_;s += blank_sep;s += to_string(y_);*content = s;return true;
#elseJson::Value root;root["x"] = x_;root["y"] = y_;root["op"] = op_;Json::FastWriter w;*content = w.write(root);return true;
#endif}bool Deserialize(string &in) // 反序列化 将字节流解析{
#ifdef Myselfint l = in.find(blank_sep);if (l == string::npos)return false;string x = in.substr(0, l); // 左闭,右开int r = in.rfind(blank_sep);if (r == string::npos)return false;string y = in.substr(r + 1);if (l + 2 != r)return false;x_ = stoi(x);y_ = stoi(y);op_ = in[l + 1];return true;
#elseJson::Reader R;Json::Value root;bool ret = R.parse(in.c_str(), root);if (!ret)return false;x_ = root["x"].asInt();y_ = root["y"].asInt();op_ = root["op"].asInt();return true;#endif}void Printf(){cout << "任务请求" << x_ << op_ << y_ << "=?" << endl;}public:int x_;int y_;char op_;
};class Respone
{
public:Respone(int res, int c) : result_(res), code_(c){}Respone(){}~Respone(){}public:bool Serialize(string *out) // 序列化 形成字节流{
#ifdef Myselfstring s = to_string(result_);s += blank_sep;s += to_string(code_);*out = s;return true;
#elseJson::Value root;root["result"] = result_;root["code"] = code_;Json::FastWriter w;*out = w.write(root);return true;
#endif}bool Deserialize(string &in) // 反序列化 字节流解析{
#ifdef Myselfint pos = in.find(blank_sep);if (pos == string::npos)return false;string x = in.substr(0, pos);int result = stoi(x);string y = in.substr(pos + 1);int code = stoi(y);result_ = result;code_ = code;return true;
#elseJson::Reader R;Json::Value root;bool ret = R.parse(in.c_str(), root);if (!ret)return false;result_=root["result"].asInt();code_=root["code"].asInt();return true;
#endif}void Printf(){cout << "result " << result_ << "  code " << code_ << endl;}public:int result_;int code_;
};
4.Tcpserver.hpp
#pragma once
#include <functional>
#include <signal.h>
#include "log.hpp"
#include "Socket.hpp"
#include "ServerCal.hpp"
#include "Protocal.hpp"using func_t=function<string(string&)>;
extern Log lg;class Server
{
public:Server(uint16_t port,func_t calback):port_(port) ,calback_(calback){}bool Init(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();lg("info","init server .... done");return true;}void Start(){signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);while(true){string clientip;uint16_t clientport;int sockfd=listensock_.Accpect(&clientip,&clientport);if(sockfd<0){lg("fatal","accpect fail");break;}lg("info","accpect success");if(fork()==0){listensock_.Close();string inbuffer_stream;while(true){char buffer[1024];int n=read(sockfd,buffer,sizeof(buffer));if(n>0){buffer[n]=0;inbuffer_stream+=buffer;lg("Debug", "debug:\n%s", inbuffer_stream.c_str());while(true){string ret=calback_(inbuffer_stream);if(ret.empty())break;lg("Dedug","debug:\n%s",ret.c_str());lg("Dedug","debug:\n%s",inbuffer_stream.c_str());write(sockfd,ret.c_str(),ret.size());}}else {cout<<"错误了"<<endl;break;}}exit(0);}close(sockfd);}}private:Sock listensock_;uint16_t port_;func_t calback_;
};
5.Tcpserver.cpp
#include "Tcpserver.hpp"
#include "ServerCal.hpp"void Usage(string prco)
{cout<<prco<<" [1024++]"<<endl;
}int main(int argc,char*argv[])
{if(argc!=2){Usage(argv[0]);exit(0);}uint16_t port=stoi(argv[1]);ServerCal calculator;Server* tsrv=new Server(port,bind(&ServerCal::Calculator,&calculator,placeholders::_1));//第一个是this指针//不用bind的话可以创建一个ServerCal的对象。tsrv->Init();tsrv->Start();return 0;
}

其中用到的bind 是 C++11 引入的一个函数模板,用于创建一个新的可调用对象,将函数的某些参数“绑定”到具体值,然后返回一个可以稍后调用的函数。

基本用法:

std::bind(function, arg1, arg2, ..., argN);
  • function:要绑定的函数。

  • arg1, arg2, ...:绑定的参数,可以是具体值或占位符(std::placeholders)。

#include <iostream>
#include <functional>int add(int x, int y) {return x + y;
}int main() {// 绑定第一个参数为 5,第二个参数用占位符auto add5 = std::bind(add, 5, std::placeholders::_1);std::cout << add5(3) << std::endl;  // 输出 8 (5 + 3)return 0;
}
  • 简化函数调用std::bind 可以将函数和参数绑定,生成新函数,便于后续调用。

  • 灵活性:支持部分绑定函数参数,允许在调用时提供剩余的参数。

lambda 比较:

// 使用 std::bind
auto add5 = std::bind(add, 5, std::placeholders::_1);// 使用 lambda
auto add5 = [](int x) { return add(5, x); };

http://www.xdnf.cn/news/1457785.html

相关文章:

  • 多Agent协作案例:用AutoGen实现“写代码+测Bug”的自动开发流程
  • 秒店功能更新:多维度优化升级,助力商家经营
  • 当 LLM 遇上真实世界:MCP-Universe 如何撕开大模型 “工具能力” 的伪装?
  • 记录相机触发相关
  • 机器学习入门,第一个MCP示例
  • (D题|矿井突水水流漫延模型与逃生方案)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 生成式引擎优化(GEO):数字营销新标配,企业如何抢占AI搜索流量高地?
  • Trae + MCP : 一键生成专业封面的高阶玩法——自定义插件、微服务编排与性能调优
  • 设计模式六大原则2-里氏替换原则
  • Linux —— 环境变量
  • mysql中find_in_set()函数的使用, ancestors字段,树形查询
  • AI视频画质提升效果实用指南:提升清晰度的完整路径
  • [论文阅读] 软件工程 | REST API模糊测试的“标准化革命”——WFC与WFD如何破解行业三大痛点
  • 【论文阅读】-《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》
  • AutoLayout与Masonry:简化iOS布局
  • (E题|AI 辅助智能体测)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 解密llama.cpp:Prompt Processing如何实现高效推理?
  • Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装
  • Scikit-learn Python机器学习 - 特征预处理 - 归一化 (Normalization):MinMaxScaler
  • 孩子学手机里的坏毛病,怎样限制他打开某些APP?
  • Flutter 3.35.2 以上版本中 数字转字符串的方法指南
  • 机器学习基础-day05-深度学习框架PyTorch的tensor及PyTorch进行线性回归
  • 猫头虎AI 荐研|腾讯开源长篇叙事音频生成模型 AudioStory:统一模型,让 AI 会讲故事
  • 数据结构 之 【哈希的相关概念】
  • npm/pnpm软链接的优点和使用场景
  • 2025精选榜:4款好用的企业即时通讯软件推荐!安全有保障
  • 【Proteus仿真】AT89C51单片机中断系列仿真——INT0中断控制LED小灯/INT0和INT1中断控制数码管
  • 小白也能看懂,HTTP中的文件上传与下载到底发生了什么?
  • Spring 框架(IoC、AOP、Spring Boot) 的必会知识点汇总
  • 2025 年高教社杯全国大学生数学建模竞赛C 题 NIPT 的时点选择与胎儿的异常判定 完整成品思路模型代码分享,全网首发高质量!!!