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

怎么做门户网站设计公司资质等级

怎么做门户网站,设计公司资质等级,网站优化外包费用,联科三网合一网站建设系统 作者#xff1a;დ旧言~ 座右铭#xff1a;松树千年终是朽#xff0c;槿花一日自为荣。 目标#xff1a;了解什么是序列化和分序列#xff0c;并且自己能手撕网络版的计算器。 毒鸡汤#xff1a;有些事情#xff0c;总是不明白#xff0c;所以我不… 作者დ旧言~ 座右铭松树千年终是朽槿花一日自为荣。 目标了解什么是序列化和分序列并且自己能手撕网络版的计算器。 毒鸡汤有些事情总是不明白所以我不会坚持。早安! 专栏选自网络 望小伙伴们点赞收藏✨加关注哟 ​​ 一、前言 前面我们已经学习了网络的基础知识对网络的基本框架已有认识算是初步认识到网络了如果上期我们的学习网络是步入基础知识那么这次学习的板块就是基础知识的实践我们今天的板块是学习网络重要之一学习完这个板块对虚幻的网络就不再迷茫 二、主体 学习【网络】自定义协议——序列化和反序列化咱们按照下面的图解 2.1 自定义协议应用层 概念 网络协议是通信计算机双方必须共同遵从的一组约定为了使数据在网络上能够从源地址到目的地址网络通信的参与方必须遵循相同的规则因此我们将这套规则称为协议协议最终需要通过计算机语言的方式表示出来。 应用层和传输层的概述 应用层位于网络协议的最高层直接为用户提供服务。这一层的协议如HTTP、FTP、SMTP等负责处理特定应用程序的数据传输和通信。传输层位于应用层之下网络层之上。它提供端到端的通信服务确保数据在网络中的可靠传输。传输层协议如TCP和UDP管理数据包的顺序和流量控制。 应用层和传输层的区别 目的应用层的目的是为了支持各种应用程序的特定需求而传输层则致力于提供通用的通信服务。可靠性应用层协议可以根据应用程序的需求提供可靠或不可靠的服务。而传输层协议如TCP保证数据包的顺序和完整性提供可靠的端到端通信。数据传输方式应用层协议如HTTP、FTP等通常基于TCP或UDP传输数据。而传输层协议则负责管理这些数据包的发送和接收。服务类型应用层协议服务于各种类型的应用如网页浏览、电子邮件、文件传输等。而传输层协议主要为应用程序提供通用的数据传输服务。 应用层和传输层的关系 应用层和传输层在网络通信中是紧密相关的。传输层提供了一种可靠的数据传输机制确保数据在网络中的正确传输而应用层协议定义了特定应用程序的数据格式和通信规则。两者之间的协同工作使得应用程序能够有效地进行数据交换和通信。 例如当我们使用浏览器访问一个网页时HTTP应用层协议定义了请求和响应的格式而TCP传输层协议确保了数据的可靠传输。两者共同作用使得我们能够顺利地浏览网页。 2.2 序列化和反序列化 2.2.1 结构化数据 概念说明 如果需要传输的数据是字符串那么我们可以直接将这个字符串发送到网络当中。但是如果我们传输的是一些结构化的数据此时就不能直接将这些数据发送到网络当中。 如果服务器将这些结构化的数据单独一个一个发送到网络当中那么服务器从网络当中获取这些数据时也只能一个一个获取并且结构化的数据往往具有特定的格式和规范例如数据库表格或者特定的数据模型。如果直接将这些数据发送到网络服务端可能需要处理大量复杂的格式转换和数据清洗工作而且还有可能会影响数据的正确性。所以客户端最好把这些结构化的数据打包后统一发送到网络当中此时服务端每次从网络当中获取到的就是一个完整的请求数据。 常见的打包方式 将结构化的数据组合成一个字符串 客户端可以按照某种方式将这些结构化的数据组合成一个字符串然后将该字符串发送到网络当中当服务器接收到这个字符串时以相同的方式将这个字符串进行解析。就可以从这个字符串中提取出这些结构化的数据。  定制结构体 序列化与反序列化 客户端可以定制一个结构体将需要交互的信息定义到这个结构体当中。客户端发送数据时先对数据进行序列化服务端接收到数据后在对其进行反序列化此时服务端就能得到客户端发送过来的结构体了。   2.2.2 序列化和反序列化 概念 序列化 将对象的状态信息转换为可以存储或传输的形式的过程。反序列化 将字节序列恢复为的过程。 补充说明 OSI七层模型中表示层的作用就是实现设备固有数据格式和网络标准数据格式的转换。其中设备固有的数据格式指的是数据在应用层上的格式而网络标准数据格式则指的是序列化之后可以进行网络传输的数据格式。 序列化和反序列化的目的 序列化的目的是方便网络数据的发送和接收无论何种类型的数据经过序列化后都变成了二进制序列此时底层在进行网络传输时看到的都是二进制序列。序列化后的二进制序列只有在网络传输时能够被底层识别上层应用是无法识别序列化后的二进制数据的因此需要将从网络中获取到的数据进行反序列化将二进制序列的数据转换成应用层能够识别的数据格式。 2.3 网络版计算器 2.3.1 协议定制 请求数据和响应数据实现一个请求结构体和一个响应结构体 请求数据结构体包含两个操作数和一个操作符操作数表示需要进行计算的两个数操作符表示是要进行 -*/ 中的哪一个操作。响应数据结构体需要包含一个计算结果和一个状态码表示本次计算的结果和计算状态。 态码所表示的含义 0 表示计算成功1 表示出现除0错误2 表示模0错误3 表示非法计算 因为协议定制好以后要被客户端和服务端同时遵守所以需要将它写到一个头文件中同时被客户端和服务端包含该头文件 // 请求 class Request { public:Request(){}Request(int x, int y, char op):_x(x), _y(y), _op(op){} public: int _x;int _y;char _op; };// 响应 class Response { public:Response(){}Response(int result, int code):_result(result), _code(code){} public:int _result;int _code; };同时最好还是把协议中的分隔符给定义出来方便后续统一使用或更改 #define CRLF \t //分隔符 #define CRLF_LEN strlen(CRLF) //分隔符长度 #define SPACE //空格 #define SPACE_LEN strlen(SPACE) //空格长度#define OPS -*/% //运算符 2.3.2 编码解码 编码的是往字符串的开头添加上长度和分隔符 长度\t序列化字符串\t 解码就是将长度和分隔符去掉只解析出序列化字符串 序列化字符串 编码解码的整个过程在注释里面都写明了 为了方便请求和回应去使用直接放到外头不做类内封装 //参数len为in的长度是一个输出型参数。如果为0代表err std::string decode(std::string in,size_t*len) {assert(len);//如果长度为0是错误的// 1.确认in的序列化字符串完整分隔符*len0;size_t pos in.find(CRLF);//查找\t第一次出现时的下标//查找不到errif(pos std::string::npos){return ;//返回空串}// 2.有分隔符判断长度是否达标// 此时pos下标正好就是标识大小的字符长度std::string inLenStr in.substr(0,pos);//从下标0开始一直截取到第一个\t之前//到这里我们要明白我们这上面截取的是最开头的长度也就是说我们截取到的一定是个数字这个是我们序列化字符的长度size_t inLen atoi(inLenStr.c_str());//把截取的这个字符串转intinLen就是序列化字符的长度//传入的字符串的长度 - 第一个\t前面的字符数 - 2个\tsize_t left in.size() - inLenStr.size()- 2*CRLF_LEN;//原本预计的序列化字符串长度if(leftinLen){//真实的序列化字符串长度和预计的字符串长度进行比较return ; //剩下的长度序列化字符串的长度没有达到标明的长度}// 3.走到此处字符串完整开始提取序列化字符串std::string ret in.substr(posCRLF_LEN,inLen);//从posCRLF_LEN下标开始读取inLen个长度的字符串——即序列化字符串*len inLen;// 4.因为in中可能还有其他的报文下一条// 所以需要把当前的报文从in中删除方便下次decode避免二次读取size_t rmLen inLenStr.size() ret.size() 2*CRLF_LEN;//长度2个\t序列字符串的长度in.erase(0,rmLen);//移除从索引0开始长度为rmLen的字符串// 5.返回return ret; }//编码不需要修改源字符串所以const。参数len为in的长度 std::string encode(const std::string in,size_t len) {std::string ret std::to_string(len);//将长度转为字符串添加在最前面作为标识retCRLF;retin;retCRLF;return ret; } 2.3.3 request请求 2.3.3.1 构造 功能说明 用户可能输入xyx yx y,x y等等格式 这里还需要注意用户的输入不一定是标准的XY里面可能在不同位置里面会有空格。为了统一方便处理在解析之前最好先把用户输入内的空格给去掉 代码呈现 class Request { public:// 将用户的输入转成内部成员// 用户可能输入xyx yx y,x y等等格式// 提前修改用户输入主要还是去掉空格提取出成员Request(std::string in,bool* status):_x(0),_y(0),_ops( ){rmSpace(in);//删除空格// 这里使用c的字符串因为有strtokchar buf[1024];// 打印n个字符多的会被截断snprintf(buf,sizeof(buf),%s,in.c_str());//将报文存到buf里面去方便使用strtokchar* left strtok(buf,OPS);//left变成从字符串in的最开始到第一个运算符的这段字符串——即左操作数if(!left){//找不到*status false;return;}//right变成从第一个运算符开始到第二个运算符中间的这段字符串——即右操作数char*right strtok(nullptr,OPS);//在上次寻找的基础上继续寻找if(!right){//找不到*status false;return;}// xy, strtok会将设置为\0char mid in[strlen(left)];//截取出操作符//这是在原字符串里面取出来buf里面的这个位置被改成\0了_x atoi(left);_y atoi(right);_ops mid;*statustrue;}public:int _x;int _y;char _ops; }; 2.3.3.2 序列化 功能说明 解析出成员以后我们要做的就是对成员进行序列化将其按指定的位置摆成一个字符串。这里采用了输出型参数的方式来序列化字符串也可以改成用返回值的方式来操作。 代码呈现 // 序列化 入参应该是空的 void serialize(std::string out) {// x yout.clear(); // 序列化的入参是空的out std::to_string(_x);out SPACE;out _ops;//操作符不能用tostring会被转成asciiout SPACE;out std::to_string(_y);// 不用添加分隔符这是encode要干的事情 } 2.3.3.3 反序列化 说明 在客户端和服务端都需要使用request客户端进行序列化服务端对接收到的结果利用request进行反序列化。request只关注于对请求的处理而不处理服务器的返回值。 代码呈现 // 反序列化 bool deserialize(const std::string in) {// x y 需要取出xy和操作符size_t space1 in.find(SPACE); //第一个空格if(space1 std::string::npos) //没找到{return false;}size_t space2 in.rfind(SPACE); //第二个空格if(space2 std::string::npos) //没找到{return false;}// 两个空格都存在开始取数据std::string dataX in.substr(0,space1);std::string dataY in.substr(space2SPACE_LEN);//默认取到结尾std::string op in.substr(space1SPACE_LEN,space2 -(space1SPACE_LEN));if(op.size()!1){return false;//操作符长度有问题}//没问题了转内部成员_x atoi(dataX.c_str());_y atoi(dataY.c_str());_ops op[0];return true; } 2.3.4 response响应 2.3.4.1 构造 说明 返回值的构造比较简单因为是服务器处理结果之后的操作这些成员变量都设置为了公有方便后续修改。 代码呈现 class Response//服务端必须回应 {Response(int code0,int result0):_exitCode(code),_result(result){}public:int _exitCode; //计算服务的退出码int _result; // 结果 }; 2.3.4.2 序列化 功能说明 解析出成员以后我们要做的就是对成员进行序列化将其按指定的位置摆成一个字符串。这里采用了输出型参数的方式来序列化字符串也可以改成用返回值的方式来操作。 代码呈现 class Response//服务端必须回应 {Response(int code0,int result0):_exitCode(code),_result(result){}// 入参是空的void serialize(std::string out){// code retout.clear();out std::to_string(_exitCode);out SPACE;out std::to_string(_result);out CRLF;}public:int _exitCode; //计算服务的退出码int _result; // 结果 }; 2.3.4.3 反序列化 说明 响应的反序列化只需要处理一个空格相对来说较为简单。 代码呈现 class Response//服务端必须回应 {Response(int code0,int result0):_exitCode(code),_result(result){}//序列化void serialize(std::string out){// code retout.clear();out std::to_string(_exitCode);out SPACE;out std::to_string(_result);out CRLF;}// 反序列化bool deserialize(const std::string in){// 只有一个空格size_t space in.find(SPACE);//寻找第一个空格的下标if(space std::string::npos)//没找到{return false;}std::string dataCode in.substr(0,space);std::string dataRes in.substr(spaceSPACE_LEN);_exitCode atoi(dataCode.c_str());_result atoi(dataRes.c_str());return true;}public:int _exitCode; //计算服务的退出码int _result; // 结果 }; 2.3.5 完整的自定义协议代码 使用说明 客户端发送的消息是使用Request来进行序列化和反序列化的  客户端发消息时客户端先序列化再编码服务端收消息时服务端先解码再反序列化 服务端发送的消息是使用Response来进行序列化和反序列化的 服务端发消息时客户端先序列化再编码客户端收消息时服务端先解码再反序列化 代码呈现Serialization.hpp #pragma #define CRLF \t // 分隔符 #define CRLF_LEN strlen(CRLF) // 分隔符长度 #define SPACE // 空格 #define SPACE_LEN strlen(SPACE) // 空格长度 #define OPS -*/% // 运算符#include iostream #include string #include cstring #includeassert.h//参数len为in的长度是一个输出型参数。如果为0代表err std::string decode(std::string in,size_t*len) {assert(len);//如果长度为0是错误的// 1.确认in的序列化字符串完整分隔符*len0;size_t pos in.find(CRLF);//查找\t第一次出现时的下标//查找不到errif(pos std::string::npos){return ;//返回空串}// 2.有分隔符判断长度是否达标// 此时pos下标正好就是标识大小的字符长度std::string inLenStr in.substr(0,pos);//从下标0开始一直截取到第一个\t之前//到这里我们要明白我们这上面截取的是最开头的长度也就是说我们截取到的一定是个数字这个是我们序列化字符的长度size_t inLen atoi(inLenStr.c_str());//把截取的这个字符串转intinLen就是序列化字符的长度//传入的字符串的长度 - 第一个\t前面的字符数 - 2个\tsize_t left in.size() - inLenStr.size()- 2*CRLF_LEN;//原本预计的序列化字符串长度if(leftinLen){//真实的序列化字符串长度和预计的字符串长度进行比较return ; //剩下的长度序列化字符串的长度没有达到标明的长度}// 3.走到此处字符串完整开始提取序列化字符串std::string ret in.substr(posCRLF_LEN,inLen);//从posCRLF_LEN下标开始读取inLen个长度的字符串——即序列化字符串*len inLen;// 4.因为in中可能还有其他的报文下一条// 所以需要把当前的报文从in中删除方便下次decode避免二次读取size_t rmLen inLenStr.size() ret.size() 2*CRLF_LEN;//长度2个\t序列字符串的长度in.erase(0,rmLen);//移除从索引0开始长度为rmLen的字符串// 5.返回return ret; }//编码不需要修改源字符串所以const。参数len为in的长度 std::string encode(const std::string in,size_t len) {std::string ret std::to_string(len);//将长度转为字符串添加在最前面作为标识retCRLF;retin;retCRLF;return ret; }class Request//客户端使用的 { public:// 将用户的输入转成内部成员// 用户可能输入xyx yx y,x y等等格式// 提前修改用户输入主要还是去掉空格提取出成员Request(std::string in, bool *status): _x(0), _y(0), _ops( ){rmSpace(in); // 删除空格// 这里使用c的字符串因为有strtokchar buf[1024];// 打印n个字符多的会被截断snprintf(buf, sizeof(buf), %s, in.c_str()); // 将报文存到buf里面去方便使用strtokchar *left strtok(buf, OPS); // left变成从字符串in的最开始到第一个运算符的这段字符串——即左操作数if (!left){ // 找不到*status false;return;}// right变成从第一个运算符开始到第二个运算符中间的这段字符串——即右操作数char *right strtok(nullptr, OPS); // 在上次寻找的基础上继续寻找if (!right){ // 找不到*status false;return;}// xy, strtok会将设置为\0char mid in[strlen(left)]; // 截取出操作符// 这是在原字符串里面取出来buf里面的这个位置被改成\0了_x atoi(left);_y atoi(right);_ops mid;*status true;}// 删除输入中的空格void rmSpace(std::string in){std::string tmp;for (auto e : in){if (e ! ){tmp e;}}in tmp;}// 序列化 入参应该是空的会返回一个序列化字符串void serialize(std::string out)//这个是客户端在发送消息给服务端时使用的在这之后要先编码才能发送出去{// x yout.clear(); // 序列化的入参是空的out std::to_string(_x);out SPACE;out _ops; // 操作符不能用tostring会被转成asciiout SPACE;out std::to_string(_y);// 不用添加分隔符这是encode要干的事情}//序列化之后应该要编码去加个长度// 反序列化解开bool deserialize(const std::string in)//这个是服务端接收到客户端发来的消息后使用的在这之前要先解码{// x y 需要取出xy和操作符size_t space1 in.find(SPACE); // 第一个空格if (space1 std::string::npos) // 没找到{return false;}size_t space2 in.rfind(SPACE); // 第二个空格if (space2 std::string::npos) // 没找到{return false;}// 两个空格都存在开始取数据std::string dataX in.substr(0, space1);std::string dataY in.substr(space2 SPACE_LEN); // 默认取到结尾std::string op in.substr(space1 SPACE_LEN, space2 - (space1 SPACE_LEN));if (op.size() ! 1){return false; // 操作符长度有问题}// 没问题了转内部成员_x atoi(dataX.c_str());_y atoi(dataY.c_str());_ops op[0];return true;}public:int _x;int _y;char _ops; };class Response // 服务端必须回应 {Response(int code 0, int result 0): _exitCode(code), _result(result){}// 序列化void serialize(std::string out)//这个是服务端发送消息给客户端使用的使用之后要编码{// code retout.clear();out std::to_string(_exitCode);out SPACE;out std::to_string(_result);out CRLF;}// 反序列化bool deserialize(const std::string in)//这个是客户端接收服务端消息后使用的使用之前要先解码{// 只有一个空格size_t space in.find(SPACE); // 寻找第一个空格的下标if (space std::string::npos) // 没找到{return false;}std::string dataCode in.substr(0, space);std::string dataRes in.substr(space SPACE_LEN);_exitCode atoi(dataCode.c_str());_result atoi(dataRes.c_str());return true;}public:int _exitCode; // 计算服务的退出码int _result; // 结果 }; 2.3.6 客户端/服务端完整代码 CalculatorClient.cc #include TcpClient.hppstatic void usage(std::string proc) {std::cout Usage:\n\t proc serverip serverport\n std::endl; }enum {LEFT,OPER,RIGHT };Request ParseLine(const std::string line) {std::string left, right;char op;int status LEFT;int i 0;while(i line.size()){switch (status){case LEFT:if (isdigit(line[i]))left.push_back(line[i]);elsestatus OPER;break;case OPER:op line[i];status RIGHT;break;case RIGHT:if (isdigit(line[i]))right.push_back(line[i]);break;}}Request req;req._x std::stoi(left);req._y std::stoi(right);req._op op;return req; }int main(int argc, char* argv[]) {if(argc ! 3){usage(argv[0]);exit(USAGE_ERR);}string serverip argv[1];uint16_t serverport atoi(argv[2]);Sock sock;sock.Socket();int n sock.Connect(serverip, serverport);if(n ! 0) return 1;string buffer;while(true){cout Enter# ;string line;getline(cin, line);Request req ParseLine(line);cout test: req._x req._op req._y endl;// 序列化string sendString;req.Serialize(sendString);// 添加报头sendString AddHeader(sendString);// sendsend(sock.Fd(), sendString.c_str(), sendString.size(), 0);// 获取响应string package;int n 0;START:n ReadPackage(sock.Fd(), buffer, package);if(n 0)goto START;else if(n 0)break;else{}// 去掉报头package RemoveHeader(package, n);// 反序列化Response resp;resp.Deserialize(package);cout result: resp._result [code: resp._code ] endl;}sock.Close();return 0; } CalculatorServer.cc #include TcpServer.hpp #include memory using namespace tcpserver_ns;Response calculate(const Request req) {// 走到这里一定保证req是有具体数据的Response resp(0, 0);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 (req._y 0)resp._code 1;elseresp._result req._x / req._y;break;case %:if (req._y 0)resp._code 2;elseresp._result req._x % req._y;break;default:resp._code 3;break;}return resp; }int main() {uint16_t port 8080;unique_ptrTcpServer tsvr(new TcpServer(calculate, port));tsvr-InitServer();tsvr-Start();return 0; } Err.hpp #pragma onceenum {USAGE_ERR 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR,CONNECT_ERR,SETSID_ERR,OPEN_ERR }; Log.hpp #pragma once #include iostream #include string #include cstring #include cstdio #include ctime #include unistd.h #include sys/types.h #include stdarg.h using namespace std;const string filename log/tcpserver.log;// 枚举日志等级 enum {Debug 0, // 调试信息Info, // 正常运行Warning, // 报警Error, // 正常错误Fatal, // 严重错误Uknown };// 字符串形式获取日志等级 static string toLevelString(int level) {switch (level){case Debug:return Debug;case Info:return Info;case Warning:return Warning;case Error:return Error;case Fatal:return Fatal;default:return Uknown;} }// 获取日志产生时间 static string getTime() {time_t curr time(nullptr);struct tm* tmp localtime(curr);// 缓冲区char buffer[128];snprintf(buffer, sizeof buffer, %d-%d-%d %d:%d:%d, tmp-tm_year 1900, tmp-tm_mon 1, tmp-tm_mday,tmp-tm_hour, tmp-tm_min, tmp-tm_sec);return buffer; }// 日志格式: 日志等级 时间 pid 消息体 —— 日志函数void logMessage(int level, const char* format, ...) {char logLeft[1024];string level_string toLevelString(level);string curr_time getTime();snprintf(logLeft, sizeof(logLeft), [%s] [%s] [%d] , level_string.c_str(), curr_time.c_str(), getpid());char logRight[1024];va_list p;va_start(p, format);vsnprintf(logRight, sizeof(logRight), format, p);va_end(p);// 打印日志printf(%s%s\n, logLeft, logRight);// 将日志保存到文件中// FILE* fp fopen(filename.c_str(), a);// if(!fp) return;// fprintf(fp, %s%s\n, logLeft, logRight);// fflush(fp);// fclose(fp); } Protocol.hpp #pragma once #include iostream #include string #include vector #include cstring #include Sock.hpp #include jsoncpp/json/json.h#include Util.hpp // 给网络版本计算器定制协议// #define MYSELF 1// 给网络版本计算器定制协议 namespace protocol_ns {#define SEP #define SEP_LEN strlen(SEP) // 注意不能写成 sizeof#define HEADER_SEP \r\n#define HEADER_SEP_LEN strlen(\r\n)// 添加报头后 —— 长度\r\n_x _op _y\r\n string AddHeader(const string str){std::cout AddHeader 之前:\n str std::endl;string s to_string(str.size());s HEADER_SEP;s str;s HEADER_SEP;std::cout AddHeader 之后:\n s std::endl;return s;}// 去除报头 长度\r\n_x _op _y\r\n —— _x _op _ystring RemoveHeader(const std::string str, int len){std::cout RemoveHeader 之前:\n str std::endl;string res str.substr(str.size() - HEADER_SEP_LEN - len, len);return res;std::cout RemoveHeader 之后:\n res std::endl;}int ReadPackage(int sock, string inbuffer, string*package){std::cout ReadPackage inbuffer 之前:\n inbuffer std::endl;// 边读取char buffer[1024];ssize_t s recv(sock, buffer, sizeof(buffer - 1), 0);if(s 0)return -1;buffer[s] 0;inbuffer buffer;std::cout ReadPackage inbuffer 之中:\n inbuffer std::endl;// 边读取边分析 7\r\n10 20\r\nauto pos inbuffer.find(HEADER_SEP); // 查找 \r\nif(pos string::npos)return 0;string lenStr inbuffer.substr(0, pos); // 获取头部字符串int len Util::toInt(lenStr);int targetPackageLen lenStr.size() len 2 * HEADER_SEP_LEN; // 获取到的完整字符串的长度if(inbuffer.size() targetPackageLen)return 0;*package inbuffer.substr(0, targetPackageLen);inbuffer.erase(0, targetPackageLen); std::cout ReadPackage inbuffer 之后:\n inbuffer std::endl;return len; // 返回有效载荷的长度}// Request Response都要提供序列化和反序列化功能——自己手写// 请求class Request{public:Request(){}Request(int x, int y, char op):_x(x), _y(y), _op(op){}// 协议的序列化 struct - stringbool Serialize(string* outStr){*outStr ; #ifdef MYSELFstring x_string to_string(_x);string y_string to_string(_y);// 手动序列化*outStr x_string SEP _op SEP y_string;std::cout Request Serialize:\n *outStr std::endl; #elseJson::Value root; // Value 是一种万能对象, 可以接受任意的kv类型root[x] _x;root[y] _y;root[op] _op;// Json::FastWriter writer; // Writer 是用来序列化的, struct - stringJson::StyledWriter writer;*outStr writer.write(root); #endifreturn true;}// 协议的反序列化 string - structbool Deserialize(const string inStr){ #ifdef MYSELF// 将inStr分隔到数组里面 - [0]10 [1] [2]20vectorstring result;Util::StringSplit(inStr, SEP, result);if(result.size() ! 3)return false;if(result[1].size() ! 1)return false;_x Util::toInt(result[0]);_y Util::toInt(result[2]);_op result[1][0]; #elseJson::Value root;Json::Reader reader; // Reader是用来进行反序列化的reader.parse(inStr, root);_x root[x].asInt();_y root[y].asInt();_op root[op].asInt(); #endifreturn true;}~Request(){}public: int _x; // 操作数 _xint _y; // 操作数 _ychar _op;// 操作符 _op};// 响应class Response{public:Response(){}Response(int result, int code):_result(result), _code(code){}bool Serialize(string* outStr){*outStr ; #ifdef MYSELFstring res_string to_string(_result);string code_string to_string(_code);// 手动序列化*outStr res_string SEP code_string;std::cout Response Serialize:\n *outStr std::endl; #elseJson::Value root;root[result] _result;root[code] _code;// Json::FastWriter writer;Json::StyledWriter writer;*outStr writer.write(root); #endifreturn true;}bool Deserialize(const string inStr){ #ifdef MYSELF// 将inStr分隔到数组里面 - [0]10 [1] [2]20vectorstring result;Util::StringSplit(inStr, SEP, result);if(result.size() ! 2)return false;_result Util::toInt(result[0]);_code Util::toInt(result[1]); #elseJson::Value root;Json::Reader reader;reader.parse(inStr, root);_result root[result].asInt();_code root[code].asInt(); #endifPrint();return true;}void Print(){std::cout _result: _result std::endl;std::cout _code: _code std::endl;}public:int _result;int _code;}; } Sock.hpp #pragma once#include iostream #include string #include cstdlib #include cstring #include sys/types.h /* See NOTES */ #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include Log.hpp #include Err.hppusing namespace std; static const int gbacklog 32; static const int defaultfd -1;class Sock { public:Sock():_sock(defaultfd){}Sock(int sock):_sock(sock){}// 创建套接字void Socket(){_sock socket(AF_INET, SOCK_STREAM, 0);if(_sock 0){logMessage(Fatal, socket error, code: %d, errstring: %s, errno, strerror(errno));exit(SOCKET_ERR);}}// 绑定端口号 和 ip地址void Bind(const uint16_t port){struct sockaddr_in local;local.sin_addr.s_addr INADDR_ANY; // 绑定任意IP地址local.sin_port htons(port); // 绑定端口号local.sin_family AF_INET;if(bind(_sock, (struct sockaddr*)local, sizeof(local)) 0){logMessage(Fatal, bind error, code: %d, errstring: %s, errno, strerror(errno));exit(BIND_ERR);}}// 监听void Listen(){if(listen(_sock, gbacklog) 0){logMessage(Fatal, listen error, code: %d, errstring: %s, errno, strerror(errno));exit(LISTEN_ERR);}}// 获取连接int Accept(string* clientip, uint16_t* clientport){struct sockaddr_in temp;socklen_t len sizeof(temp);int sock accept(_sock, (struct sockaddr*)temp, len);if(sock 0){logMessage(Warning, accept error, code: %d, errstring: %s, errno, strerror(errno));}else{*clientip inet_ntoa(temp.sin_addr);*clientport ntohs(temp.sin_port);}return sock;}// 客户端和服务器建立连接int Connect(const string serverip, const uint16_t serverport){struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport);server.sin_addr.s_addr inet_addr(serverip.c_str());return connect(_sock, (struct sockaddr*)server, sizeof(server));}int Fd(){return _sock;}void Close(){if(_sock ! defaultfd)close(_sock);}~Sock(){}private:int _sock; };TcpClient.hpp #pragma once#include TcpClient.hpp #include Sock.hpp #include Protocol.hpp#include iostream #include stringusing namespace std; using namespace protocol_ns; TcpServer.hpp #pragma once #include iostream #include functional #include cstring #include pthread.h #include Sock.hpp #include Protocol.hppusing namespace std;namespace tcpserver_ns {using namespace protocol_ns;using func_t functionResponse(const Request);class TcpServer;class ThreadData{public:ThreadData(int sock, string ip, uint16_t port, TcpServer* tsvrp):_sock(sock), _ip(ip), _port(port), _tsvrp(tsvrp){}~ThreadData(){}public:int _sock;string _ip;uint16_t _port;TcpServer *_tsvrp;};class TcpServer{public:TcpServer(func_t func, uint16_t port):_func(func), _port(port){}// 初始化服务器void InitServer(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();logMessage(Info, init server done, listensock: %d, errstring: %s, errno, strerror(errno));}// 运行服务器void Start(){while(true){string clientip;uint16_t clientport;int sock _listensock.Accept(clientip, clientport);if(sock 0) continue;logMessage(Debug, get a new client, client info : [%s:%d], clientip.c_str(), clientport);pthread_t tid; // 创建多线程ThreadData *td new ThreadData(sock, clientip, clientport, this);pthread_create(tid, nullptr, ThreadRoutine, td);}}static void* ThreadRoutine(void* args){pthread_detach(pthread_self());ThreadData *td static_castThreadData *(args);td-_tsvrp-ServiceIO(td-_sock, td-_ip, td-_port);logMessage(Debug, thread quit, client quit ...);delete td;return nullptr;}// 服务器对客户端的数据进行IO处理void ServiceIO(int sock, const std::string ip, const uint16_t port){string inbuffer;while(true){// 保证自己读到一个完整的字符串报文 7\r\n10 20\r\nstring package;int n ReadPackage(sock, inbuffer, package);if(n -1)break;else if(n 0)continue;else{// 已经得到了一个7\r\n10 20\r\n// 1. 提取有效载荷package RemoveHeader(package, n);// 2. 已经读到了一个完整的stringRequest req;req.Deserialize(package);// 3. 直接提取用户的请求数据Response resp _func(req); // 业务逻辑// 4. 给用户返回响应——序列化string send_string;resp.Serialize(send_string);// 5. 添加报头send_string AddHeader(send_string);// 6. 发送send(sock, send_string.c_str(), send_string.size(), 0);}}close(sock);}~TcpServer(){}private:uint16_t _port; // 端口号Sock _listensock; // 监听套接字func_t _func;}; }Util.hpp #pragma once #include iostream #include string #include vector #include cstdlibusing namespace std;class Util { public:static bool StringSplit(const stringstr, const string sep, vectorstring* result){size_t start 0;while(start str.size()){auto pos str.find(sep, start);if(pos string::npos) break;result-push_back(str.substr(start, pos - start));start pos sep.size();}if(start str.size())result-push_back(str.substr(start));return true;}static int toInt(const string s){return atoi(s.c_str());} };makefile .PHONY:all all:calserver calclientcalclient:CalculatorClient.ccg -o $ $^ -stdc11 -ljsoncppcalserver:CalculatorServer.ccg -o $ $^ -stdc11 -lpthread -ljsoncpp.PHONY:clean clean:rm -f calclient calserver 三、结束语  今天内容就到这里啦时间过得很快大家沉下心来好好学习会有一定的收获的大家多多坚持嘻嘻成功路上注定孤独因为坚持的人不多。那请大家举起自己的小手给博主一键三连有你们的支持是我最大的动力回见。 ​​ 
文章转载自:
http://www.morning.lhjmq.cn.gov.cn.lhjmq.cn
http://www.morning.cxsdl.cn.gov.cn.cxsdl.cn
http://www.morning.dbylp.cn.gov.cn.dbylp.cn
http://www.morning.fslrx.cn.gov.cn.fslrx.cn
http://www.morning.sxhdzyw.com.gov.cn.sxhdzyw.com
http://www.morning.sfwcx.cn.gov.cn.sfwcx.cn
http://www.morning.gwjnm.cn.gov.cn.gwjnm.cn
http://www.morning.smmby.cn.gov.cn.smmby.cn
http://www.morning.mtktn.cn.gov.cn.mtktn.cn
http://www.morning.pxlql.cn.gov.cn.pxlql.cn
http://www.morning.8yitong.com.gov.cn.8yitong.com
http://www.morning.ygbq.cn.gov.cn.ygbq.cn
http://www.morning.yhsrp.cn.gov.cn.yhsrp.cn
http://www.morning.pzqnj.cn.gov.cn.pzqnj.cn
http://www.morning.lokext.com.gov.cn.lokext.com
http://www.morning.ttrdr.cn.gov.cn.ttrdr.cn
http://www.morning.xnhnl.cn.gov.cn.xnhnl.cn
http://www.morning.pqktp.cn.gov.cn.pqktp.cn
http://www.morning.cszbj.cn.gov.cn.cszbj.cn
http://www.morning.rfdqr.cn.gov.cn.rfdqr.cn
http://www.morning.bcdqf.cn.gov.cn.bcdqf.cn
http://www.morning.hrpbq.cn.gov.cn.hrpbq.cn
http://www.morning.gtmdq.cn.gov.cn.gtmdq.cn
http://www.morning.rqqn.cn.gov.cn.rqqn.cn
http://www.morning.gxcym.cn.gov.cn.gxcym.cn
http://www.morning.cldgh.cn.gov.cn.cldgh.cn
http://www.morning.fqnql.cn.gov.cn.fqnql.cn
http://www.morning.mtrfz.cn.gov.cn.mtrfz.cn
http://www.morning.mqwnp.cn.gov.cn.mqwnp.cn
http://www.morning.bwhcl.cn.gov.cn.bwhcl.cn
http://www.morning.qrmry.cn.gov.cn.qrmry.cn
http://www.morning.lfxcj.cn.gov.cn.lfxcj.cn
http://www.morning.dgsr.cn.gov.cn.dgsr.cn
http://www.morning.bdgb.cn.gov.cn.bdgb.cn
http://www.morning.bxsgl.cn.gov.cn.bxsgl.cn
http://www.morning.ynryz.cn.gov.cn.ynryz.cn
http://www.morning.zxzgr.cn.gov.cn.zxzgr.cn
http://www.morning.bhjyh.cn.gov.cn.bhjyh.cn
http://www.morning.lyhry.cn.gov.cn.lyhry.cn
http://www.morning.xnkh.cn.gov.cn.xnkh.cn
http://www.morning.qclmz.cn.gov.cn.qclmz.cn
http://www.morning.lrskd.cn.gov.cn.lrskd.cn
http://www.morning.lprfk.cn.gov.cn.lprfk.cn
http://www.morning.nkbfc.cn.gov.cn.nkbfc.cn
http://www.morning.kpgbz.cn.gov.cn.kpgbz.cn
http://www.morning.dtrz.cn.gov.cn.dtrz.cn
http://www.morning.snmth.cn.gov.cn.snmth.cn
http://www.morning.xmjzn.cn.gov.cn.xmjzn.cn
http://www.morning.npxcc.cn.gov.cn.npxcc.cn
http://www.morning.nqdkx.cn.gov.cn.nqdkx.cn
http://www.morning.chzbq.cn.gov.cn.chzbq.cn
http://www.morning.ckfqt.cn.gov.cn.ckfqt.cn
http://www.morning.flzqq.cn.gov.cn.flzqq.cn
http://www.morning.ishoufeipin.cn.gov.cn.ishoufeipin.cn
http://www.morning.drswd.cn.gov.cn.drswd.cn
http://www.morning.xfxqj.cn.gov.cn.xfxqj.cn
http://www.morning.mgkcz.cn.gov.cn.mgkcz.cn
http://www.morning.qieistand.com.gov.cn.qieistand.com
http://www.morning.ljwyc.cn.gov.cn.ljwyc.cn
http://www.morning.yrnrr.cn.gov.cn.yrnrr.cn
http://www.morning.sypby.cn.gov.cn.sypby.cn
http://www.morning.zwfgh.cn.gov.cn.zwfgh.cn
http://www.morning.wfysn.cn.gov.cn.wfysn.cn
http://www.morning.nd-test.com.gov.cn.nd-test.com
http://www.morning.leyuhh.com.gov.cn.leyuhh.com
http://www.morning.ryyjw.cn.gov.cn.ryyjw.cn
http://www.morning.qxlgt.cn.gov.cn.qxlgt.cn
http://www.morning.pmdlk.cn.gov.cn.pmdlk.cn
http://www.morning.wtnyg.cn.gov.cn.wtnyg.cn
http://www.morning.zlrsy.cn.gov.cn.zlrsy.cn
http://www.morning.ftcrt.cn.gov.cn.ftcrt.cn
http://www.morning.smpmn.cn.gov.cn.smpmn.cn
http://www.morning.rgwz.cn.gov.cn.rgwz.cn
http://www.morning.qklff.cn.gov.cn.qklff.cn
http://www.morning.jqmqf.cn.gov.cn.jqmqf.cn
http://www.morning.xlbtz.cn.gov.cn.xlbtz.cn
http://www.morning.qnjcx.cn.gov.cn.qnjcx.cn
http://www.morning.krswn.cn.gov.cn.krswn.cn
http://www.morning.srcth.cn.gov.cn.srcth.cn
http://www.morning.xesrd.com.gov.cn.xesrd.com
http://www.tj-hxxt.cn/news/249655.html

相关文章:

  • 珠海建设工程备案网站墟沟企业建站价格表
  • 零基础网站建设入门到精通视频教企业网站建设基本要素
  • 网站制作自学百度云诺诚建设工程有限公司网站
  • 河北省建设中心网站手机网页打不开怎么办
  • 图片上传网站制作外包网站建设费用包括网站备份
  • 小型网站设计网站备案容易吗
  • 网站的类型用php做网站出现的问题
  • 上海电子商务网站制作做装修网站价格
  • 普通网站建设的缺陷免费网站设计 优帮云
  • 深圳做外贸网站哪家好网站推广是怎么推广的
  • 福田服务商app软件安装南宁排名seo公司
  • 五分钟自己创建网站的方法成交型网站建设公司
  • 曲靖网站开发公司浙江省通信管理局 网站备案
  • 做网站vpn多大内存能和实体彩票店和做的彩票网站
  • 舆情网站直接打开网页设计图片居中对齐的代码
  • 保险资料网站有哪些php一键建站
  • 站群系统有哪些别人带做的网站关闭了权限咋办
  • 网站国际互联网备案号wordpress添加代码
  • ASP网站开发技术期末考试应用市场下载安装app
  • 做汽车配件的都在那个网站做呀网站开发税目编码
  • 小程序推广模式和营销方案德州seo整站优化
  • 郴州网站建设系统重庆网站seo教程
  • 农业电商网站建设ppt网络设计行业是干什么的
  • 怎么做网站流量竞品分析海外服务器怎么搭建
  • 大图做网站背景加载慢指数基金怎么买才赚钱
  • 创建网站 制作首页设计公司名字创意
  • 手机网站建设广州国内地铁建设公司网站
  • 网站优化 福州wordpress采集插件 免费
  • 免费代刷网站推广dede做的网站怎样去换模版
  • 滨海网站建设氧os哪个网站做的最好