位置: IT常识 - 正文

谈谈linux网络编程中的应用层协议定制、Json序列化与反序列化那些事(linux的网络编程)

编辑:rootadmin
谈谈linux网络编程中的应用层协议定制、Json序列化与反序列化那些事 linux【网络编程】之协议定制、序列化与反序列化一、序列化与反序列化二、应用层协议如何定制三、网络通信中数据流动的本质四、网络版计算器编写4.1 业务流程4.2 核心代码一、序列化与反序列化

推荐整理分享谈谈linux网络编程中的应用层协议定制、Json序列化与反序列化那些事(linux的网络编程),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:linux网络编程采用的是哪种网络字节序,linux网络编程是什么,linux网络编程采用的是哪种网络字节序,linux网络编辑,linux网络编辑,linux网络编辑,linux网络编程用什么语言,linux的网络编程,内容如对您有帮助,希望把文章链接给更多的朋友!

由于socket api的接口,在读写数据的时候是以字符串的方式发送接收的,如果需要传输结构化的数据,就需要制定一个协议 结构化数据在发送到网络中之前需要完成序列化 接收方收到的是序列字节流,需要完成反序列化才能使用(如ChatInfo._name)

二、应用层协议如何定制

当我们进行网络通信的的时候,一端发送时构造的数据, 在另一端能够正确的进行解析(完整的读到一条报文), 就是可行的的. 这种约定, 就是 应用层协议

如何保证读到的消息是一个完整的请求 TCP是面向字节流的,无法直接读取,需要明确报文和报文的边界,常见的方法有.定长:固定报文长度、特殊符号:在报文前面加上一个字段、自描述。

三、网络通信中数据流动的本质

我们调用的所有的发送函数(read),不是把数据发送到网络中,发送函数的本质是拷贝函数(将数据从应用层缓冲区拷贝到发送缓冲区)

Client->Server:tcp发送的本质,其实就是将数据从Client的发送缓冲区拷贝到Server的接收缓冲区反过来Server->CLient:其实就是将数据从Server的发送缓冲区拷贝到 Client的接收缓冲区这也说明了网络编程(套接字编程)是全双工的四、网络版计算器编写谈谈linux网络编程中的应用层协议定制、Json序列化与反序列化那些事(linux的网络编程)

有了前面的知识,下面实现一个服务器版的计算器. 我们需要客户端把要计算的两个数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端

4.1 业务流程

在完成服务器与客户端正常通信的基础上完成一次请求与响应的流程 客户端:

从键盘读取数据并调用ParseLine()函数将输入的数据转换成类似“123+123"的格式序列化字符串,将结构化的数据转化成用于网络通信的一个大字符串,调用请求类的序列化函数enLength()添加报头:通过协议定制的规则,将"x op y"---->“content_len”\r\n"x op y"\r\n向服务端发送已经构建好的报文阻塞读取服务端处理后的响应数据 等待服务器发送响应报文读取到一个完整的报文调用协议方法去掉报头并将结果输出到text里面对收到的响应正文序列化填充到响应类对象的成员变量里通过正常调用,访问处理后的结果发送新的请求

服务端:

创建子进程去执行任务死循环式的读取来自客户端的报文,调用recvRequest,读取一个完整的请求放入输出型参数text里面根据协议定制,调用deLength()去掉报文的报头,得到有效数据req_str将来自网络的字符串通过调用请求类的反序列化转化成结构化的数据,请求类对象成员完成赋值通过回调函数(一个输入型参数(请求类对象),一个输出型参数(响应类对象))传递两个类对象,将计算结果赋值给响应类的成员变量开始将处理结果返回给客户端,调用响应类中的序列化方法将结构化数据转换成一个大字符串对大字符串添加报头构建成一个完整的报文,发送给客户端服务端等待新的请求…4.2 核心代码

Protocol.hpp包含了协议定制函数、请求响应序列化与反序列化函数、完整报文获取函数

#pragma once#include <iostream>#include <string>#include <cstring>#include <jsoncpp/json/json.h>using namespace std;#define SEP " " // 分隔符#define SEP_LEN strlen(SEP) // 分隔符长度,不能用sizeof#define LINE_SEP "\r\n"#define LINE_SEP_LINE strlen(LINE_SEP)enum{ OK = 0, DIV_ZERO, MOD_ZERO, OP_ERROR};// 协议定制:给报文段加一个特殊字段:有效载荷的长度// 报头 有效载荷// //"exitcode result"---->"content_len"\r\n"exitcode result"\r\n----std::string enlength(const std::string &text){ // text就是"x op y" string send_string = std::to_string(text.size()); // content_len send_string += LINE_SEP; send_string += text; send_string += LINE_SEP; return send_string;}// 去掉报头,提取有效载荷//"content_len"\r\n"exitcode result"\r\n---->exitcode resultbool delength(const std::string &package, std::string *text){ auto pos = package.find(LINE_SEP); if (pos == string::npos) return false; // 提取报头字符串 string text_len_string = package.substr(0, pos); // 将报头信息转化成字符串 int text_len = std::stoi(text_len_string); // 提取有效载荷 *text = package.substr(pos + LINE_SEP_LINE, text_len); return true;}// 请求class Request{public: Request() : x_(0), y_(0), op_(0) { } Request(int x, int y, char op) : x_(x), y_(y), op_(op) { } // 序列化 bool serialize(std::string *out) {#ifdef MYSELF // 将结构化数据转化成-->"x op y" out->clear(); std::string x_string = std::to_string(x_); std::string y_string = std::to_string(y_); *out = x_string; *out += SEP; *out += op_; *out += SEP; *out += y_string;#else Json::Value root;//定义一个万能对象 root["first"]=x_; root["second"]=y_; root["oper"]=op_; Json::FastWriter writer; *out=writer.write(root);#endif return true; } // 反序列化 bool deserialize(const std::string &in) { #ifdef MYSELF //"x op yyy"; auto left = in.find(SEP); auto right = in.rfind(SEP); if (left == std::string::npos || right == std::string::npos) return false; if (left == right) return false; // 截取子串 if (right - (left + SEP_LEN) != 1) return false; std::string x_string = in.substr(0, left); // 定位到x std::string y_string = in.substr(right + SEP_LEN); // 定位到yyyy if (x_string.empty()) return false; if (y_string.empty()) return false; x_ = std::stoi(x_string); y_ = std::stoi(y_string); op_ = in[left + SEP_LEN]; // 截取op#else Json::Value root; Json::Reader reader; reader.parse(in,root);//将解析出来的值放进root里面 x_=root["first"].asInt();//将val转化成整数 y_=root["second"].asInt(); op_=root["oper"].asInt();#endif return true; }public: int x_; int y_; char op_;};// 响应class Response{public: Response() : exitcode_(0), result_(0) { } Response(int exitcode, int result) : exitcode_(exitcode), result_(result) { } // 序列化 bool serialize(std::string *out) {#ifdef MYSELF // 清空字符串 out->clear(); // 将退出码和结果转换成字符串 string ec_string = std::to_string(exitcode_); string res_string = std::to_string(result_); // 合并字符 *out = ec_string; *out += SEP; *out += res_string;#else Json::Value root; root["exitcode"]=exitcode_; root["result"]=result_; Json::FastWriter writer; *out=writer.write(root);#endif return true; } // 反序列化 bool deserialize(const std::string &in) {#ifdef MYSELF //"exitcode result" auto mid = in.find(SEP); if (mid == std::string::npos) return false; // 截取字符串 string ec_string = in.substr(0, mid); string res_string = in.substr(mid + SEP_LEN); if (ec_string.empty() || res_string.empty()) return false; // 写入退出码和结果 exitcode_ = std::stoi(ec_string); result_ = std::stoi(res_string);#else Json::Reader reader; Json::Value root; reader.parse(in,root); exitcode_=root["exitcode"].asInt(); result_=root["result"].asInt();#endif return true; }public: int exitcode_; // 0成功,!0错误 int result_; // 计算结果};// 读取一个完整的请求放入text里面//"content_len"\r\n"x op y"\r\n"content_len"\r\n"x op y"\r\nbool recvRequest(int sock, std::string &inbuffer, string *text){ char buffer[1024]; while (true) { ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); if (n > 0) { buffer[n] = 0; inbuffer += buffer; // 边读边处理 auto pos = inbuffer.find(LINE_SEP); // 如果没有读到\r\n,接着去读 if (pos == string::npos) continue; // 走到这已经读到了content_len,知道了有效载荷长度 string text_len_string = inbuffer.substr(0, pos); // 报头 int text_len = std::stoi(text_len_string); // 正文长度 int total_len = text_len_string.size() + 2 * LINE_SEP_LINE + text_len; std::cout << "处理前#inbuffer: \n" << inbuffer << endl; if (inbuffer.size() < total_len) { std::cout << "你输入的消息,没有严格遵守我们的协议,正在等待后续的内容, continue" << std::endl; continue; // 没有读到一个完整的报文 } // 至少有一个报文 *text = inbuffer.substr(0, total_len); inbuffer.erase(0, total_len); std::cout << "处理后#inbuffer: \n" << inbuffer << endl; break; } else return false; } return true;}

服务端响应流程

void handlerEnter(int sock, func_t func) { string inuffer;//将所有信息写入到inbuffer while(true) { // 1.读取:"content_len"\r\n"x op y"\r\n // 1.1 保证读到的消息是【一个完整】的请求 std::string req_text; // 输出型参数,整个报文 if (!recvRequest(sock, inuffer,&req_text)) return; std::cout<<"带报头的请求:\n"<<req_text<<endl; // 1.2 去报头,只要正文 std::string req_str; // 正文部分 if (!delength(req_text, &req_str)) return; std::cout<<"去掉报头后的正文:\n"<<req_str<<endl; // 2.反序列化 // 2.1 得到一个结构化对象,对象中的成员已经被填充 Request req; if (!req.deserialize(req_str)) return; // 3.处理数据---------业务逻辑 // 3.1 得到一个结构化的响应,resp成员已被填充 Response resp; func(req, resp); // 回调 // 4.对响应Response,序列化 // 4.1 得到一个字符串 std::string resp_str; resp.serialize(&resp_str); // 输出型参数,将序列化结果写入resp_str std::cout<<"计算完成,序列化响应: "<<resp_str<<endl; // 5.然后发送响应 // 5.1添加协议报头,构建成一个完整的报文 std::string send_string = enlength(resp_str); std::cout<<"构建带报头的响应正文: \n"<<send_string<<endl; // 发送 send(sock, send_string.c_str(), send_string.size(), 0); // 有问题 std::cout<<"发送响应报文成功: \n"<<endl; } }

客户端请求流程

void run() { struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(clientport_); server.sin_addr.s_addr = inet_addr(clientip_.c_str()); // 发起链接 if (connect(sockfd_, (struct sockaddr *)&server, sizeof(server)) != 0) { std::cerr << "connect create error" << endl; } else { string msg; string inbuffer; while (true) { cout << "mycal>>> "; std::getline(std::cin, msg); Request req = ParseLine(msg); // 从键盘提取字符串 string content; // 序列化结构数据 req.serialize(&content); // 添加报头 string send_string = enlength(content); // 发送数据 send(sockfd_, send_string.c_str(), send_string.size(), 0); // 接收响应报文 string package, text; if (!recvRequest(sockfd_,inbuffer,&package)) continue; //去掉报头,获取正文放在text里面 if(!delength(package,&text)) continue; //将收到的响应正文反序列化 Response resp; resp.deserialize(text); std::cout << "exitCode: " << resp.exitcode_ << std::endl; std::cout << "result: " << resp.result_ << std::endl; } } }

正常输入输出显示如下图

以上只是提供了几个核心的代码块,完整版代码可以去我的Gitee,代码注释详细,希望对你有所帮助

本文链接地址:https://www.jiuchutong.com/zhishi/288144.html 转载请保留说明!

上一篇:新河峡国家公园中的新河峡大桥,西弗吉尼亚州 (© Entropy Workshop/iStock/Getty Images Plus)(教案的近义词是什么)

下一篇:vue中props设置默认值-父组件给子组件传值的写法——简略、带类型、带类型和默认值、带校验(vue props emit)

  • 为什么卡不在状态(卡不存在是怎么回事)

    为什么卡不在状态(卡不存在是怎么回事)

  • 手机设备锁怎么解除(手机设备锁怎么开)

    手机设备锁怎么解除(手机设备锁怎么开)

  • 苹果手机如何设置来电灯光(苹果手机如何设置密码锁屏)

    苹果手机如何设置来电灯光(苹果手机如何设置密码锁屏)

  • 三星s9发热严重怎么解决(三星s9发烫很严重怎么回事)

    三星s9发热严重怎么解决(三星s9发烫很严重怎么回事)

  • 远程操控对方电脑可以看到对方嘛(远程操控对方电脑显示黑屏怎么办)

    远程操控对方电脑可以看到对方嘛(远程操控对方电脑显示黑屏怎么办)

  • 剪映慢放动作怎么设置(剪映怎样放慢动作)

    剪映慢放动作怎么设置(剪映怎样放慢动作)

  • Excel重复值怎么找(excel重复值怎么删除只保留一个)

    Excel重复值怎么找(excel重复值怎么删除只保留一个)

  • eml是什么文件格式(eml文件是病毒么)

    eml是什么文件格式(eml文件是病毒么)

  • miui12支持哪些手机(miui12支持的机型)

    miui12支持哪些手机(miui12支持的机型)

  • 用于局域网的基本网络连接设备是(用于局域网的基础设施)

    用于局域网的基本网络连接设备是(用于局域网的基础设施)

  • 抖音极速版能发私信吗(抖音极速版能发动图吗)

    抖音极速版能发私信吗(抖音极速版能发动图吗)

  • 淘宝号为什么要实名认证呢(淘宝号为什么要重新注册)

    淘宝号为什么要实名认证呢(淘宝号为什么要重新注册)

  • it工作是啥意思(it主要做什么工作)

    it工作是啥意思(it主要做什么工作)

  • iphone7分屏功能怎么用(iphone7p如何分屏)

    iphone7分屏功能怎么用(iphone7p如何分屏)

  • vivo手机设置sos在哪里(vivo手机设置搜索)

    vivo手机设置sos在哪里(vivo手机设置搜索)

  • 抖音撤回私信对方看得见吗(抖音撤回私信对方没有记录痕迹)

    抖音撤回私信对方看得见吗(抖音撤回私信对方没有记录痕迹)

  • 微信收款商业版怎么注销(微信收款商业版和个人收款码区别)

    微信收款商业版怎么注销(微信收款商业版和个人收款码区别)

  • 网站如何制作(网站如何制作成应用)

    网站如何制作(网站如何制作成应用)

  • 汽车网站设计的技巧有哪些(汽车网站的分类及其特点)

    汽车网站设计的技巧有哪些(汽车网站的分类及其特点)

  • 农贸APP开发有什么意义(农贸批发软件)

    农贸APP开发有什么意义(农贸批发软件)

  • 如何增强无线网络信号?(如何增强无线网卡的接收能力)

    如何增强无线网络信号?(如何增强无线网卡的接收能力)

  • win10电脑重装系统后屏幕显示不全有黑边怎么办?(Win10电脑重装系统要多少钱)

    win10电脑重装系统后屏幕显示不全有黑边怎么办?(Win10电脑重装系统要多少钱)

  • Yolov5网络修改教程(将backbone修改为EfficientNet、MobileNet3、RegNet等)(yolov5参数设置)

    Yolov5网络修改教程(将backbone修改为EfficientNet、MobileNet3、RegNet等)(yolov5参数设置)

  • 手把手教你用Yolov5 (v6.2) 训练分类模型 基于《Kaggle猫狗大战》案例(手把手教你用气焊视频)

    手把手教你用Yolov5 (v6.2) 训练分类模型 基于《Kaggle猫狗大战》案例(手把手教你用气焊视频)

  • Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)[保姆级]

    Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)[保姆级]

  • pdbedit命令  管理Samba服务的用户账户(pdclient)

    pdbedit命令 管理Samba服务的用户账户(pdclient)

  • 自然人收缴税务客户端
  • 环保税的纳税义务人是施工方还是建设方安徽
  • 分公司所得税汇算清缴
  • 餐饮发票数量单价必须填吗
  • 银行日记账必须一年一本吗
  • 报销费用明细和发票
  • 会计核算过程中的错误分析
  • 公司收承兑贴现的会计分录
  • 公司租用办公室需要交房产税吗
  • 现金收货款要填什么单子
  • 化验室购买仪器计入什么科目
  • 合作社 注销
  • 所得税审核费计入什么科目
  • 修理费没有发票怎么做账
  • 预缴增值税一般计税依据
  • 增值税专用发票有效期是多长时间
  • 每月发放奖金的会计分录
  • 微税平台怎么打印发票
  • 预缴的附加税还可以扣减吗
  • 销售佣金的税率怎么算
  • 还在讨论“税务金四”上线?税务和银行要联手清查单位和个人账户了!
  • 同一地级行政区范围内举例
  • 公司对公账户可以转私人账户多久到账
  • 股权转让收到钱会计分录
  • 设备定金计入哪个科目
  • 研发费用的
  • 其他应收款借方和贷方什么意思
  • 增值税罚款怎么做凭证
  • 企业结业后实收资本怎么处理?
  • 酒店垃圾处理费标准怎么确定
  • 收到押金和支付押金的账务处理
  • 华为鸿蒙双击背面打开健康码
  • linux的进程类型
  • win11如何关闭开机自启动项
  • 汇算清缴资产减值损失怎么填
  • 网络不通怎么办苹果手机
  • 升级w10系统
  • mac怎么设置默认程序
  • php数组函数大全
  • pop3服务是什么
  • 不动产固定资产标识牌
  • 4个生活小常识简短
  • 公司收到投资款怎么写收据?
  • php框架ci
  • 已缴款未入库是扣款成功了吗
  • java基础网络编程
  • 变量与数据
  • 公司年产值怎么计算
  • 固定资产折旧怎么做会计科目
  • 供货方代垫运费会计分录
  • 政府补贴流程
  • 劳务成本 科目
  • 清卡后还可以勾选发票吗
  • 收据能否入账?如何填写才具有合法性呢...
  • 事业单位如何调档案
  • 小规模收到专票可以当普票用吗
  • 房屋租赁违约金怎么规定
  • 研发费用如何做成本分析
  • 存货总账根据什么填列
  • 营改增之后还有营业税金及附加吗
  • 企业的管理费用包括
  • mysql数据库崩了怎么恢复
  • win8如何使用
  • 未知文件怎么删除
  • fedora使用
  • linux监视内存详细信息命令
  • windows有哪些
  • ubuntu 14.4
  • windows10 不能启动
  • win8怎么添加wifi
  • cocos2djs教程
  • opengl glbegin
  • jQuery Mobile和HTML5开发App推广注册页
  • linux shell -s
  • javascript里的yield
  • 中心组学习的意义?
  • 重庆地方税务局2016年2号
  • 利润表研发费用项目应根据管理费用
  • 90平房子税
  • 什么是非行政许可
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设