Linux----网络基础(2)--应用层的序列化与反序列化--守护进程--0226
文章中有使用封装好的头文件,可以在下面连接处查询。
Linux相关博文中使用的头文件_Gosolo!的博客-CSDN博客
1. 应用层
我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层
1.2 协议
我们在之前的套接字编程中使用的是基于字符串的通信,客户端connect()之后,通过getlne()来输入数据,从而通过send()发送给服务端。如果我们要传输一些"结构化的数据" 怎么办呢?
那就需要指定一些约定。这些约定构成了协议。
客户端发送一个形如"1 + 1"的字符串;
这个字符串中有两个操作数, 都是整形;
两个数字之间会有一个字符是运算符,...
定义结构体来表示我们需要交互的信息;
发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;...
2. 序列化与反序列化
2.1 序列化
以聊天软件为例,发送人、发送消息、发送时间明明是三个消息,但是通过一定手段合成一个报文统一的发给对方。
结构化的数据转为字符串数据。
2.2 反序列化
对方读到之后,将该报文再读回三个消息,分别装填到发送人、发送消息、发送时间。
字符串结构转为结构化的数据。
2.3 结构化数据和字节流式数据对比
为什么我们之前实现tcp,udp时就是传递的结构化的数据呢?
一是因为协议一旦定制完成就不会轻易更改,而这个协议可以支持网路通信使用结构化的数据。二是网络协议,有一些大小端问题,代码也有一些条件编译,会自动识别平台,设置好对齐的规则。内核级的协议定制解决了这些问题。
一般而言,结构化的数据是给上层应用来使用的。而字节流的数据更适用于网络传输,这样在上层业务和网络通信之间增加了一个软件层,使其解耦。
3.网络版计算器
要实现使用结构化的数据进行传输,首先需要有两个类,一个用于发送待计算的数字和运算方法,一个用于保存结果。假定输入格式为"x_ op_ y_",那还需要定义一个分隔符。
//封装到一个命名空间里 即没有命名冲突 到时候还可以展开 using namespace ns_protocol
namespace ns_protocol
{#define SPACE " "
#define SPACE_LEN strlen(SPACE)一个用于返回结果//请求class Request{public:Request(){}Request(int x,int y,char op):x_(x),y_(y),op_(op){}~Request() {}//序列化//反序列化public:int x_;int y_;char op_;};//回复class Response{public:Response(){}Response(int result, int code, int x, int y, char op) :result_(result),code_(code),x_(x),y_(y),op_(op){}~Response() {}//序列化//反序列化public:int result_;//返回结果int code_;//用于评判本次计算是否合法int x_;int y_;char op_;};
}
3.1 自定义协议
如果要想使用这个计算器,那么需要满足一些约定。仅规定输入格式为"_x _op _y"够用吗?
对于TCP而言,其是面向字节流的,IO读写操作,其实本质上是拷贝函数。
send || write 其实就是将我们自己定义的buffer中的内容拷贝到客户端的发送缓冲区中。
recv || read 其实就是将服务端的接收缓冲区中拷贝到我们自己定义的buffer里面。
那这些缓冲区什么时候发,发多少,出错了怎么办都是由TCP传输控制协议中决定的。所以发送的次数和接收的次数没有任何关系。
所以如果我们自主实现协议,就需要增加一些报文(特殊标识符)。来确定我们接收到的数据中,是否能组成一组“合法”的操作。
所以我们规定输入格式为 "length\r\nx_ op_ y_\r\n",至少读够一个这样的字符串才开始执行。
#define SEP "\r\n"
#define SEP_LEN strlen(SEP)
3.1.1 序列化
std::string Requst::Serialize()
{std::string str;str=std::to_string(x_);str+=SPACE;str+=op_;str+=SPACE;str+=std::to_string(y_);return str;
}
std::string Response::Serialize()
{std::string s;s = std::to_string(code_);s += SPACE;s += std::to_string(result_);return s;
}
3.1.2 反序列化
bool Requst::Deserialized(const std::string &str)
{size_t left=str.find(SPACE);if(left==std::string::npos) return false;size_t right=str.rfind(SPACE);if(right==std::string::npos) return false;//左右两个空格都找到了x_=atoi(str.substr(0,left).c_str());y_=atoi(str.substr(right+SPACE_LEN).c_str());if (left + SPACE_LEN > str.size())return false;elseop_ = str[left + SPACE_LEN];return true;
}
bool Respon::Deserialized(const std::string &s)
{std::size_t pos = s.find(SPACE);if (pos == std::string::npos)return false;code_ = atoi(s.substr(0, pos).c_str());result_ = atoi(s.substr(pos + SPACE_LEN).c_str());return true;
}
3.2 使用 json定制协议
安装json
sudo yum install jsoncpp-devel
json的使用
用{}的KV结构,支持数组
3.3 添加报文
namespace ns_protocol
{//添加报文std::string Encode(std::string &s){std::string new_package = std::to_string(s.size());new_package += SEP;new_package += s;new_package += SEP;return new_package;}//删除报文// "length\r\nx_ op_ y_\r\n..." //std::string Decode(std::string &buffer){std::size_t pos = buffer.find(SEP);if(pos == std::string::npos) return "";//拿到报文前面那个length int size = atoi(buffer.substr(0, pos).c_str());//拿到真实的有效载荷的长度int surplus = buffer.size() - pos - 2*SEP_LEN;if(surplus >= size){//至少具有一个合法完整的报文, 可以动手提取了buffer.erase(0, pos+SEP_LEN);std::string s = buffer.substr(0, size);//提取完成了 但是删干净 不影响下次进行判断buffer.erase(0, size + SEP_LEN);return s;}else{return "";}}
}
3.4 接受信息和发送信息
因为客户端和用户端都需要调用recv()和send(),而且这两端都需要包括协议文件,所以将这两个接口封装到这里。
namespace ns_protocol
{ bool Recv(int sock, std::string *out){char buffer[1024];ssize_t s = recv(sock, buffer, sizeof(buffer)-1, 0); // 9\r\n123+789\r\nif (s > 0){buffer[s] = 0;*out += buffer;}else if (s == 0){std::cout << "client quit" << std::endl;return false;}else{std::cout << "recv error" << std::endl;return false;}return true;}void Send(int sock, const std::string str){std::cout << "send in" << std::endl;int n = send(sock, str.c_str(), str.size(), 0);if (n < 0)std::cout << "send error" << std::endl;}}
4.客户端 服务端的代码逻辑
4.1 服务端 CalServer.cc
#include "TcpServer.hpp"
#include "Protocol.hpp"#include <memory>
#include <signal.h>using namespace ns_tcpserver;//这个是TcpServer.hpp内的命名空间
using namespace ns_protocol;static void Usage(const std::string &process)
{std::cout << "\nUsage: " << process << " port\n"<< std::endl;
}static Response calculatorHelper(const Request &req)
{Response resp(0, 0, req.x_, req.y_, req.op_);switch (req.op_){case '+':resp.result_ = req.x_ + req.y_;break;case '-':resp.result_ = req.x_ - req.y_;break;case '*':resp.result_ = req.x_ * req.y_;break;case '/':if (0 == req.y_)resp.code_ = 1;elseresp.result_ = req.x_ / req.y_;break;case '%':if (0 == req.y_)resp.code_ = 2;elseresp.result_ = req.x_ % req.y_;break;default:resp.code_ = 3;break;}return resp;
}//返回值类型为void 参数类型为int 符合包装器
void calculator(int sock)
{std::string inbuffer;while(true){//1.读取数据bool res=Recv(sock,&inbuffer);if(!res) break;std::cout << "begin: inbuffer: " << inbuffer << std::endl;//2.协议解析,得到一个完整的报文std::string package=Decode(inbuffer);if(package.empty()){//进行下一次读取continue;}logMessage(NORMAL, "%s", package.c_str());Request req;//3.进行反序列化,将字符数据存进结构数据req.Deserialized(package);//4.对这些数据进行处理Response resp = calculatorHelper(req);//5. 再序列化回去std::string respString= resp.Serialize();//6.添加报文respString = Encode(respString);//7.发送数据Send(sock, respString);}}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(1);}// 一般服务器,都是要忽略SIGPIPE信号的,防止在运行中出现非法写入而发生抛异常signal(SIGPIPE, SIG_IGN);std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));server->BindService(calculator);//屏蔽信号是为了这里 server->Start();return 0;
}
4.2 服务端 CalClient.cc
#include <iostream>
#include "Sock.hpp"
#include "Protocol.hpp"using namespace ns_protocol;static void Usage(const std::string &process)
{std::cout << "\nUsage: " << process << " serverIp serverPort\n"<< std::endl;
}// ./client server_ip server_port
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string server_ip = argv[1];uint16_t server_port = atoi(argv[2]);Sock sock;int sockfd = sock.Socket();//连接if (!sock.Connect(sockfd, server_ip, server_port)){std::cerr << "Connect error" << std::endl;exit(2);}bool quit = false;std::string buffer;while (!quit){// 1. 获取需求Request req;std::cout << "Please Enter # ";std::cin >> req.x_ >> req.op_ >> req.y_;// 2. 序列化std::string s = req.Serialize();// 3. 添加长度报头s = Encode(s);// 4. 发送给服务端Send(sockfd, s);// 5. 正常读取while (true){bool res = Recv(sockfd, &buffer);if (!res){quit = true;break;}//去除报文std::string package = Decode(buffer);if (package.empty())continue;Response resp;//反序列化resp.Deserialized(package);std::string err;switch (resp.code_){case 1:err = "除0错误";break;case 2:err = "模0错误";break;case 3:err = "非法操作";break;default:std::cout << resp.x_ << resp.op_ << resp.y_ << " = " << resp.result_ << " [success]" << std::endl;break;}if(!err.empty()) std::cerr << err << std::endl;break;}}close(sockfd);return 0;
}
4.3 运行截图
注意不能少打空格 有bug
5. 守护进程
我们之前写的程序,全部都是在前台进行的,即启动服务器必须输入
./CalServer 8080
如果我退出了XShell 这个进程就退出了。如果我们不想让它退出,就需要让它守护进程化。
一些概念:
- 前台进程:和终端关联的进程。
判断一个进程是不是前台进程:看能否处理你的输入。
- 任何xshell登录,只允许一个前台进程和多个后台进程。
- 进程除了有自己的pid,ppid,还有一个组id。
- 在命令行中,同时用管道启动多个进程,多个进程是兄弟进程,而同时被创建的多个进程可以成为一个进程组的概念,组长一般是第一个进程。
- 任何一次登录,登录的用户,需要有多个进程(组),来给这个用户提供服务。用户也可以自己启动很多进程(组)。我们把给用户提供服务的进程或者用户自己启动的所有的进程或者服务,整体归纳到一个叫做会话的机制中。
守护进程化其实就是让进程变成一个单独的会话。使退出bash也不会影响这个进程的存活。
如何将自己变成自成会话呢?
5.1 setsid
#include <unistd.h>
pid_t setsid(void);
让自己自成会话并变成这个进程组的组长。成功返回pid 失败返回-1
注:
- setsid要被成功调用,必须保证当前进程不是进程组的组长。所以需要配合fork()使用。
- 守护进程不能直接向显示器打印消息,一旦打印会被暂停、终止。
5.2 写一个函数 让调用的进程变成守护进程
目标:
1.忽略SIGPIPE,SIGCHLD。
2.不要让自己成为组长 ——fork()
3.调用setsid()
4.标准输入、标准输出、标准错误的重定向。——重定向到哪里呢? /dev/null
/dev/null 该文件是一个写入自动丢弃,读入的时候不阻塞但是什么都读不到。
#pragma once#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>void MyDaemon()
{// 1. 忽略信号,SIGPIPE,SIGCHLDsignal(SIGPIPE, SIG_IGN);signal(SIGCHLD, SIG_IGN);// 2. 不要让自己成为组长if (fork() > 0)exit(0);// 3. 调用setsidsetsid();// 4. 标准输入,标准输出,标准错误的重定向,守护进程不能直接向显示器打印消息int devnull = open("/dev/null", O_RDONLY | O_WRONLY);if(devnull > 0){dup2(0, devnull);dup2(1, devnull);dup2(2, devnull);close(devnull);}
}
使用
相关文章:

Linux----网络基础(2)--应用层的序列化与反序列化--守护进程--0226
文章中有使用封装好的头文件,可以在下面连接处查询。 Linux相关博文中使用的头文件_Gosolo!的博客-CSDN博客 1. 应用层 我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 1.2 协议 我们在之前的套接字编程中使用的是…...

uipath实现滑动验证码登录
现实需求 在进行RPA流程设计过程中,遇到登录系统需要滑动验证的情况,如图所示: 此时需要在RPA流程设计中,借助现有的活动完成模拟人工操作,完成验证登录操作。 设计思路 这个功能流程的设计思路大体如下: …...

openai-chatGPT的API调用异常处理
因为目前openai对地区限制的原因,即使设置了全局代理使用API调用时,还是会出现科学上网代理的错误问题。openai库 0.26.5【错误提示】:raise error.APIConnectionError(openai.error.APIConnectionError: Error communicating with OpenAI: …...

css实现音乐播放器页面 · 笔记
效果 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width, …...

buu [NPUCTF2020]这是什么觅 1
题目描述: 就一个这种文件,用记事本打开后: 题目分析: 打开后就一串看不懂的东西,想想这个东西曾经在 010editor 或 winhex中出现过(右端)既然如此那么我们就用它打开,得到&#…...
Restful API 设计规范
1. 简介 目前 "互联网软件"从用客户端/服务端模式,建立在分布式体系上,通过互联网通讯,具有高延时、高开发等特点。但是软件开发和网络是两个不同的领域,交集很少。要使得两个融合,就要考虑如何在互联网环境…...
sigwaittest测试超标的调试过程
1,问题描述硬件环境:飞腾S2500(64核)OS:kylinOS, linux preempt rt, 4.19.90测试命令:sigwaittest -p 90 -i 1000 -a 1测试结果:信号混洗值最大超过了80us,与飞腾其他CPU…...

Python进阶-----面对对象4.0(面对对象三大特征之--继承)
目录 前言: Python的继承简介 1.什么是继承 2.继承的好处 3.object类 继承的相关用法 1.继承的定义与法则 2.对继承的重写 3.(单继承)多层继承 4.多继承 5.多继承重写时调用父类方法 前言: 在讲之前,我想说说中…...

九龙证券|利好政策密集发布,机构扎堆看好的高增长公司曝光
新能源轿车销量和保有量快速增长,带来了充电桩商场的微弱需求。 日前,商务部部长王文涛表明,本年将在落实好方针的一起,活跃出台新方针办法,比方辅导当地展开新能源轿车下乡活动,优化充电等使用环境&#x…...

stm32CubeIDE FMC 驱动LCD(8080)
一,TFT屏硬件接口16位,80并口。二,FMC介绍。FSMC(Flexible Static Memory Controller),译为灵活的静态存储控制器。STM32F1 系列芯片使用 FSMC 外设来管理扩展的存储器,它可以用于驱动包括 SRAM…...

Java 数据类型
数据类型用于对数据归类,以便开发者理解和操作。 基本数据类型 Java 确定了每种基本数据类型所占存储空间的大小,不会像其它语言那样随机器硬件架构的变化而变化,这使 Java 程序更具可移植性。 Java 中定义了如下的基本数据类型。 byte …...

Prometheus 监控云Mysql和自建Mysql(多实例)
本文您将了解到 Prometheus如何配置才能监控云Mysql(包括阿里云、腾讯云、华为云)和自建Mysql。 Prometheus 提供了很多种Exporter,用于监控第三方系统指标,如果没有提供也可以根据Exporter规范自定义Exporter。 本文将通过MySQL server exporter 来监控…...

Vue3中的h函数
文章目录简介简单使用参数使用计数器进阶使用函数组件插槽专栏目录请点击 简介 众所周知,vue内部构建的其实是虚拟DOM,而虚拟DOM是由虚拟节点生成的,实质上虚拟节点也就是一个js对象事实上,我们在vue中写的template,最终也是经过…...
阿尔法开发板 IMX6ULL 说明
一. IMX6ULL开发板 IMX6ULL开发板即正点原子的阿尔法(ALPHA)开发板,采用恩智浦芯片,cortex-A7架构的。 二. IM6ULL开发板说明 1. IO说明 对于IMX6ULL芯片,一个IO对应两个寄存器,第一个寄存器负责配置其复用功能,…...

Altium Designer19 #学习笔记# | 基础应用技巧汇总
全文目录一.元件符号库二.元件封装库1.AD09 集成元件库/封装库三.电路原理图1. 巧用查找"相似对象功能"1.1 查找相同元件1.2. 查找相同文本1.3. 查找相同网络 :E - S - C四.PCB原理图【AD PCB模式下的常用快捷键】PCB视图放大/缩小PCB视图左/右移动PCB切换…...

Python 元类编程实现一个简单的 ORM
概述 什么是ORM? ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。 现在我们就要实…...
《C++ Primer Plus》第18章:探讨 C++ 新标准(7)
C11 新增的其他功能 C11 增加了很多功能,本书无法全面介绍;另外,本书编写期间,其中很多功能还未得到广泛实现。然而,有些功能有必要简要地介绍一下。 并行编程 当前,为提高计算机性能,增加处…...

Redis学习(二):Redis安装测试
概述 Redis是什么 Redis, Remote Dictionary Server, 即远程字典服务。免费开源的数据库。 由C语言编写,支持网络,可基于内存亦可持久化的日志型、KV数据库,并提供所种语言的API。 Redis能干嘛 用于内存存储,持久化。rdb、ao…...

Vector - CAPL - 简介及数据结构
对于想进入车载行业或者已经在车载行业工作的朋友对于CAPL这个词都会相当的熟悉,都知道他是做车载网络测试脚本的语言,并且跟C有点类似,但是它到底是什么呢?CAPL全称(Communication Access Programming Language&#…...
20230304英语学习
What Would Happen if the Moon Disappeared Tomorrow? 如果明天月球消失了会怎样? The closest object to our planet, the Moon, may seem like Earth’s little sibling.Since its birth, the satellite has mostly just hung around, playing gravitational t…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...