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

水利部建设管理司网站seo网站优化案例

水利部建设管理司网站,seo网站优化案例,怎么做日本钓鱼网站吗,网络推广的定义目录 一,关于“协议” 1.1 结构化数据 1.2 序列化和反序列化 二,网络版计算器实现准备 2.1 套用旧头文件 2.2 封装sock API 三,自定义协议 3.1 关于自定义协议 3.2 实现序列化和反序列化 3.3 测试 三,服务器实现 3.1…

目录

一,关于“协议”

1.1 结构化数据

1.2 序列化和反序列化

二,网络版计算器实现准备

2.1 套用旧头文件

2.2 封装sock API

三,自定义协议

3.1 关于自定义协议

3.2  实现序列化和反序列化

3.3 测试

三,服务器实现

3.1 逻辑梳理

3.2 各头文件实现

四,客户端实现


一,关于“协议”

1.1 结构化数据

两个主机通过网络和协议进行通信时,发送的数据有两种形式:

  • 如果传输的数据直接就是一个字符串,那么把这个字符串发出去,对方也能得到这个字符串
  • 如果需要传输的是一个struct结构体,那么不能将结构体数据一个个发送到网络中

比如我要实现一个网络版的计算器,那么客户端给服务器发送的数据,就要包含左操作数,运算符和右操作数,那么这就不仅仅是一个字符串了,而是一组数据

所以客户端不能把这些数据一个个发送过去,需要把这些数据“打个包”,统一发到网络中,此时服务器就能获取到一个完整的数据请求,“打包”方式有两种:

方案一:将结构化的数据结合成一个大的字符串

  • 比如我要发送“1+1”,用户输入的是“整型”,“字符”,“整型”
  • 我们先用to_string函数把整型转为字符串,然后用strcat或者C++/string的 "+="运算符重载将这三个字符拼接成一个长字符串,然后就可以直接发送
  • 最后服务器收到了长字符串,再以相同的方式进行拆分,用stoi函数将字符串转整型,就可以提取这些结构化的数据

方案二:定制结构化数据,实现序列化和反序列化 

  • 客户端可以定制一个结构体,将需要交互的信息放到结构体种
  • 客户端发送前,将结构体的数据进行序列化,服务器收到数据后进行反序列化,此时服务器就能得到客户端发送过来的结构体,下面我们来详细讲讲序列化和反序列化

1.2 序列化和反序列化

  •  序列化是将对象的状态信息转换为可以存储或传输的形式(字节序列)的过程
  • 反序列化就是把序列化的字节序列恢复为对象的过程

OSI七层模型中表示层的作用,就是“实现数据格式和网络标准数据格式的转换”。前者数据格式就是指数据再应用层上的格式,后者就是指序列化之后可以进行网络传输的数据格式 

  •  序列化的目的,是为了方便网络数据的发送和接收,序列化后数据就全变成了二进制数据,此时底层在进行数据传输时看到的统一都是二进制序列
  • 我发的是二进制数据,所以对方收到的也是二进制数据,所以需要进行反序列化,将二进制数据转化为上层能够识别的比如字符串,整型数据

二,网络版计算器实现准备

前置博客:计算机网络(三) —— 简单Udp网络程序-CSDN博客

计算机网络(四) —— 简单Tcp网络程序-CSDN博客

下面我们来全程手搓一个网络版计算器服务,并且我们自己实现一个自定义协议,主要是为了感受一下协议的实现,后面我们就不会再自定义协议了,直接用现成的

2.1 套用旧头文件

源代码下载:计算机网络/自定义协议——网络版计算器 · 小堃学编程/Linux学习 - 码云 - 开源中国 (gitee.com)

网络版计算器我们要用到的头文件有以下几个:

 

 我们先把前面写的头文件套用一下:

makefile

.PHONY:all
all:servercal clientcalFlag=#-DMySelf=1
Lib=-ljsoncpp #这个是后面使用json头文件时要用的servercal:ServerCal.ccg++ -o $@ $^ -std=c++11 $(Lib) $(Flag)
clientcal:ClientCal.ccg++ -o $@ $^ -std=c++11 -g $(Lib) $(Flag).PHONY:clean
clean:rm -f clientcal servercal

Log.hpp

#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};Log log;

Deamon.hpp

#pragma once#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string nullfile = "/dev/null";void Daemon(const std::string &cwd = "")
{// 1. 忽略其他异常信号signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2. 将自己变成独立的会话if (fork() > 0)exit(0);setsid();// 3. 更改当前调用进程的工作目录if (!cwd.empty())chdir(cwd.c_str());// 4. 标准输入,标准输出,标准错误重定向至/dev/nullint fd = open(nullfile.c_str(), O_RDWR);if (fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}

2.2 封装sock API

在Udp和Tcp服务器编写时,可以发现在使用sock API以及填装sockaddr结构体时,步骤都非常相似,所以我们可以把这些相似的步骤都封装起来,下面是Socket.hpp的代码:

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"#include <cstring>enum{SocketErr = 2,BindErr,ListenErr,
};const int backlog = 10;class Sock
{
public:Sock(){}~Sock(){}public:void Socket() // 创建套接字{_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){log(Fatal, "socket error, %s: %d", strerror(errno), errno);exit(SocketErr);}}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;if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0) // 如果小于0就绑定失败{log(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}void Listen() // 监听套接字{if (listen(_sockfd, backlog) < 0) // 如果小于0就代表监听失败{log(Fatal, "listen error, %s: %d", strerror(errno), errno);exit(ListenErr);}}int Accept(std::string *clientip, uint16_t *clientport) // 获取连接,参数做输出型参数{struct sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(_sockfd, (struct sockaddr *)(&peer), &len);if (newfd < 0) // 获取失败{log(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr)); // 把网络字节序列转化为字符串保存在ipstr数组里供用户读取*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd;}bool Connect(const std::string &ip, const uint16_t port){struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));int n = connect(_sockfd, (struct sockaddr *)&peer, sizeof(peer));if (n == -1){std::cerr << "connect to " << ip << ":" << port << "error" << std::endl;return false;}return true;}void Close(){close(_sockfd);}int Fd(){return _sockfd;}private:int _sockfd;
};

三,自定义协议

3.1 关于自定义协议

在之前的文章中介绍过,任何的网络协议,都要提供两种功能,下面是博客的截图:计算机网络(一) —— 网络基础入门_计算机网络基础教程-CSDN博客

网络版计算器,用户会在命令行输入三个字符:"1","+","1",然后我们可以拼接成一个长字符串:"1 + 1",数字与运算符通过一个空格隔开,

但是,如果客户端连续发了两个字符串,那么最终服务器收到的报文就是“1 + 12 + 1”,可以发现,两个字符串粘在了一起,所以我们的自定义协议,不仅仅要提供将报文和有效载荷分离的能力,也要提供将报文与报文分开的能力,有下面两种方法:

  • 方案一,用特殊字符隔开报文与报文 --> "1 + 1" \n "2 + 2"
  • 方案二,在报文前面加上报文的长度,也就是报头 --> "9"\n"100 + 200"\n,这样就为一个完整的报文(其实只要有长度就可以了,这里增加\n是为了可读性,也是为了方便后面打印)

所以下面来梳理一下我们自定义协议的序列化和反序列化全流程:

3.2  实现序列化和反序列化

这个部分就是具体实现Protocol.hpp头文件了,这个文件具体包含下面几个内容:

  1. "100","+","200" --> "100 + 200"
  2. "100 + 200" --> "9"\n"100 + 200"
  3.  "9"\n"100 + 200" --> "100 + 200"
  4. "100 + 200" --> "100","+","200"

该文件包含两个类,一个类是请求类,是客户端发给服务器用到的类;另一个类是响应类,是服务器处理完后,返回给客户端的类;此外还包括两个方法,分别是封装报头将报头和有效载荷分离

Request类:

#pragma once
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>#define MySelf 0 // 去掉注释就是用我们自己的序列化和反序列化,加上注释就是用json库提供的const std::string blank_space = " "; // 分隔符
const std::string protocol_sep = "\n";class Request // 计算的请求
{
public:Request(int data1, int data2, char oper): x(data1), y(data2), op(oper){}Request(){}~Request(){}public:bool Serialize(std::string *out) // 序列化{
#ifdef MySelf// 1,构建报文的有效载荷//  需要把结构化的数据转化为字符串 struct --> string, "x op y"std::string s = std::to_string(x);s += blank_space;s += op;s += blank_space;s += std::to_string(y);// 走到这里的时候就是字符串 "x op y"// 但是在传输的时候可能发过来的不是完整的一个报文:"10 + 20",而是只有半个报文:"10 + "// 解决方案一:用特殊字符隔开报文与报文 --> "10 + 20" \n "20 + 40"// 解决方案二:再在报文前面加一个字符串的长度也就是报头,例如s.size()// 结合起来就是"9"\n"100 + 200"\n,为一个完整的报文,其实只要有长度就可以了,这里增加\n是为了可读性,也是为了方便后面// 2,封装报头*out = s;return true;
#elseJson::Value root;root["x"] = x;root["y"] = y;root["op"] = op;Json::FastWriter w;*out = w.write(root);return true;#endif}bool DeSerialize(const std::string &in) // 反序列化  "9"\n"10 + 20"{
#ifdef MySelfstd::size_t left = in.find(blank_space); // 找空格的左边,"10 + 20",也就是找10的右边位置if (left == std::string::npos)           // 没找到空格,说明当前解析错误{return false;}std::string part_x = in.substr(0, left); // 截取第一个数字,也就是10std::size_t right = in.rfind(blank_space); // 逆向再次找空格,"10 + 20",找20左边的位置if (right == std::string::npos)            // 没找到空格,说明当前解析错误{return false;}std::string part_y = in.substr(right + 1); // 截取后面的数字,也就是20,+1是因为找到的是空格的右边,+1跳过空格才是数字if (left + 2 != right)return false;  // 数字中间还有运算符,所以left+2就应该是right的左边那个空格的左边位置,如果不是那么就是解析错误op = in[left + 1]; // 拿到运算符// op = in[right - 1]; //一样的x = std::stoi(part_x); // 拿到数字y = std::stoi(part_y);return true;
#elseJson::Value root;Json::Reader r;r.parse(in, root);x = root["x"].asInt();y = root["y"].asInt();op = root["op"].asInt();return true;
#endif}void DebugPrint(){std::cout << "新请求构建完成:  " << x << " " << op << " " << y << "=?" << std::endl;}public:int x;int y;char op; // 运算符
};class Response // 计算的应答
{
public:Response(int res, int c): result(res), code(c){}Response(){}~Response(){}public:bool Serialize(std::string *out) // 序列化{
#ifdef MySelf// 1,构建报文的有效载荷//"len"\n"result code"std::string s = std::to_string(result);s += blank_space;s += std::to_string(code);*out = s;return true;
#elseJson::Value root;root["result"] = result;root["code"] = code;// Json::FastWriter w;Json::StyledWriter w;*out = w.write(root);return true;
#endif}bool DeSerialize(const std::string &in) // 反序列化{
#ifdef MySelf// 对服务器发过来的结果报文做解析: "result code"std::size_t pos = in.find(blank_space); // 找空格的左边if (pos == std::string::npos)           // 没找到空格,说明当前解析错误{return false;}std::string part_left = in.substr(0, pos);   // 截取第一个数字,也就是resultstd::string part_right = in.substr(pos + 1); // 截取后面第二个数字,也就是coderesult = std::stoi(part_left);code = std::stoi(part_right);return true;
#elseJson::Value root;Json::Reader r;r.parse(in, root);result = root["result"].asInt();code = root["code"].asInt();return true;
#endif}void DebugPrint(){std::cout << "结果响应完成, result: " << result << ", code: " << code << std::endl;}public:int result; // x op yint code;   // 错误码,为0时结果正确,为其它数时对应的数表示对应的原因
};

 Response类:

class Response // 计算的应答
{
public:Response(int res, int c): result(res), code(c){}Response(){}~Response(){}public:bool Serialize(std::string *out) // 序列化{
#ifdef MySelf// 1,构建报文的有效载荷//"len"\n"result code"std::string s = std::to_string(result);s += blank_space;s += std::to_string(code);*out = s;return true;
#elseJson::Value root;root["result"] = result;root["code"] = code;// Json::FastWriter w;Json::StyledWriter w;*out = w.write(root);return true;
#endif}bool DeSerialize(const std::string &in) // 反序列化{
#ifdef MySelf// 对服务器发过来的结果报文做解析: "result code"std::size_t pos = in.find(blank_space); // 找空格的左边if (pos == std::string::npos)           // 没找到空格,说明当前解析错误{return false;}std::string part_left = in.substr(0, pos);   // 截取第一个数字,也就是resultstd::string part_right = in.substr(pos + 1); // 截取后面第二个数字,也就是coderesult = std::stoi(part_left);code = std::stoi(part_right);return true;
#elseJson::Value root;Json::Reader r;r.parse(in, root);result = root["result"].asInt();code = root["code"].asInt();return true;
#endif}void DebugPrint(){std::cout << "结果响应完成, result: " << result << ", code: " << code << std::endl;}public:int result; // x op yint code;   // 错误码,为0时结果正确,为其它数时对应的数表示对应的原因
};

 添加和去掉报头函数:

std::string Encode(const std::string &content) // 添加报头
{std::string packge = std::to_string(content.size()); // 加报头packge += protocol_sep;                              // 加\npackge += content;                                   // 加正文packge += protocol_sep;                              // 再加\nreturn packge;
}bool Decode(std::string &package, std::string *content) // 解析并去掉报头 "9"\n"10 + 20"\n -->"10 + 20"  俗称解包,但是只是去掉了报头,没有做报文的具体解析
{std::size_t pos = package.find(protocol_sep); // 找到\n的左边if (pos == std::string::npos)return false;                             // 解析失败std::string len_str = package.substr(0, pos); // 从开始截到我找到的\n处,把前面的9给截出来std::size_t len = std::stoi(len_str);         // 把截出来的报头转化为size_t,也就是把字符串9转化成数字9// packge的长度 = 报头长度len_str + 有效载荷长度content_str + 两个\n 2std::size_t total_len = len_str.size() + len + 2;// ①找到了第一个\n说明一定有长度,如果没找到\n就说明连报头都没有// ②有了长度报头,你也还得保证后面的内容也是完整的,如果不完整也就是长度不一样,那我也就不玩了if (package.size() < total_len)return false;// 走到这一步说明我们能保证报文是完整的,开始拿有效载荷*content = package.substr(pos + 1, len); // pos现在是第一个\n左边的位置,+1后面的就是正文内容,正文内容长度为len// 移除一个报文,该功能需要和网络相结合package.erase(0, total_len);return true;
}

3.3 测试

我们可以在ServerCal.cc文件里测试上面我们的序列化和反序列化操作

先测试Request:

ServerCal.cc:

#include "Log.hpp"
#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ServerCal.hpp"
#include "Deamon.hpp"int main()
{// Request测试--------------------Request req(10, 20, '+');std::string s;req.Serialize(&s);std::cout << "有效载荷为: " << s << std::endl;s = Encode(s);std::cout << "报文为:" << s;std::string content;bool r = Decode(s, &content); //分离报头和有效载荷std::cout << "分离报头后的有效载荷为: "<< content << std::endl;Request temp;temp.DeSerialize(content); //解析有效载荷std::cout<< "有效载荷分离后, x为: " << temp.x << " 运算符为:\"" << temp.op << "\"  y为: " << temp.y << std::endl;return 0;
}

然后是Response的测试: 

ServerCal.cc:

#include "Log.hpp"
#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ServerCal.hpp"
#include "Deamon.hpp"int main()
{// Response测试--------------------Response resp(10, 20);std::string s;resp.Serialize(&s);std::cout << "有效载荷为: " << s << std::endl;std::string package = Encode(s); //分离报头和有效载荷std::cout << "报文为: " << package;s = "";bool r = Decode(package, &s);std::cout << "分离报头后的有效载荷为: " << s << std::endl;Response temp;temp.DeSerialize(s); // 解析有效载荷std::cout << "解析有效载荷: " << std::endl;std::cout << "结果为: " << temp.result << std::endl;std::cout << "错误码为: " << temp.code << std::endl;return 0;
}

三,服务器实现

3.1 逻辑梳理

服务器涉及两个个头文件和一个源文件,有点绕,下面先梳理一下:

有三个文件:

  • 首先,TcpServer.hpp是服务器主函数,ServerCal.cc包含服务器初始化和启动的main函数,ServerCal.hpp是进行计算器运算的头文件
  • 首先构建服务器对象,并在构造函数里将ServerCal.cc里面的运算函数带进去,然后是初始化服务器,执行创建套接字等操作,然后启动服务器
  • 当服务器收到客户端发来的报文后,直接将报文传给运算函数,由运算函数做去掉报头,解析有效载荷等过程,并执行运算,最后把运算结果再次构建成响应报文,以返回值形式返回给服务器运行函数
  • 然后服务器再把响应报文发给客户端,完成一次计算请求处理

3.2 各头文件实现

Server.hpp实现:

#pragma once
#include <iostream>
#include <string>
#include "Protocol.hpp"enum
{Div_Zero = 1,Mod_Zero,Other_Oper
};class ServerCal
{
public:ServerCal(){}~ServerCal(){}Response CalculatorHelper(const Request &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 = Div_Zero;}else{resp.result = req.x / req.y;}}break;case '%':{if (req.y == 0){resp.code = Mod_Zero;}else{resp.result = req.x % req.y;}}break;default:resp.code = Other_Oper;break;}return resp;}std::string Calculator(std::string &package){std::string content;if (!Decode(package, &content)) // 分离报头和有效载荷:"len"\n"10 + 20"\nreturn "";// 走到这里就是完整的报文Request req;if (!req.DeSerialize(content)) // 反序列化,解析有效载荷 "10 + 20" --> x=10 op="+" y=20return "";content = "";Response resp = CalculatorHelper(req); // 执行计算逻辑resp.Serialize(&content);              // 序列化计算结果的有效载荷 result=10, code=0content = Encode(content);             // 将有效载荷和报头封装成响应报文 "len"\n"30 0"return content;}
};

TcpServer.hpp实现:

#pragma once
#include "Log.hpp"
#include "Socket.hpp"
#include <signal.h>
#include <string>
#include <functional>using func_t = std::function<std::string(std::string &package)>;class TcpServer
{
public:TcpServer(uint16_t port, func_t callback): _port(port), _callback(callback){}bool InitServer(){// 创建,绑定,监听套接字_listensockfd.Socket();_listensockfd.Bind(_port);_listensockfd.Listen();log(Info, "Init server... done");return true;}void Start(){signal(SIGCHLD, SIG_IGN); // 忽略signal(SIGPIPE, SIG_IGN);while (true){std::string clientip;uint16_t clientport;int sockfd = _listensockfd.Accept(&clientip, &clientport);if (sockfd < 0)continue;log(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);// 走到了这里就是成功获取发起连接方IP与port,后面就是开始提供服务if (fork() == 0){_listensockfd.Close();// 进行数据运算服务std::string inbuffer_stream;while (true){char buffer[1280];ssize_t n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;inbuffer_stream += buffer; // 这里用+=log(Debug, "debug:\n%s", inbuffer_stream.c_str());while (true){std::string info = _callback(inbuffer_stream);// if (info.size() == 0) //ServerCal.hpp,解析报文失败的话会返回空串if (info.empty()) // 空的话代表inbuffstream解析时出问题,表示不遵守协议,发不合法的报文给我,我直接丢掉不玩了break;        // 不能用continuelog(Debug, "debug, response:\n%s", info.c_str());log(Debug, "debug:\n%s", inbuffer_stream.c_str());write(sockfd, info.c_str(), info.size());}}else if (n == 0) // 读取出错break;else // 读取出错break;}exit(0);}close(sockfd);}}~TcpServer(){}private:uint16_t _port;Sock _listensockfd;func_t _callback;
};

ServerCal.cc实现:

#include "Log.hpp"
#include "Socket.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "ServerCal.hpp"
#include "Deamon.hpp"static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << "port\n\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);ServerCal cal;TcpServer *tsvp = new TcpServer(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));tsvp->InitServer();//Daemon();//daemon(0, 0);tsvp->Start();// Request测试--------------------// Request req(10, 20, '+');// std::string s;// req.Serialize(&s);// std::cout << "有效载荷为: " << s << std::endl;// s = Encode(s);// std::cout << "报文为:" << s;// std::string content;// bool r = Decode(s, &content); //分离报头和有效载荷// std::cout << "分离报头后的有效载荷为: "<< content << std::endl;// Request temp;// temp.DeSerialize(content); //解析有效载荷// std::cout<< "有效载荷分离后, x为: " << temp.x << " 运算符为:\"" << temp.op << "\"  y为: " << temp.y << std::endl;// Response测试--------------------// Response resp(10, 20);// std::string s;// resp.Serialize(&s);// std::cout << "有效载荷为: " << s << std::endl;// std::string package = Encode(s); //分离报头和有效载荷// std::cout << "报文为: " << package;// s = "";// bool r = Decode(package, &s);// std::cout << "分离报头后的有效载荷为: " << s << std::endl;// Response temp;// temp.DeSerialize(s); // 解析有效载荷// std::cout << "解析有效载荷: " << std::endl;// std::cout << "结果为: " << temp.result << std::endl;// std::cout << "错误码为: " << temp.code << std::endl;return 0;
}

四,客户端实现

客户端的话,为了方便发送计算请求,会采用随机数的方式获取运算数和运算符,如下代码:

#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include "Socket.hpp"
#include "Protocol.hpp"static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << " serverip serverport\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]); //获取IP和端口Sock sockfd;sockfd.Socket();if (!sockfd.Connect(serverip, serverport))return 1;srand(time(nullptr) ^ getpid()); // 种随机数种子int cnt = 1;const std::string opers = "+-*/%-=&^";std::string inbuffer_stream;while (cnt <= 5){std::cout << "===============第" << cnt << "次测试....., " << "===============" << std::endl;int x = rand() % 100 + 1;usleep(1234);int y = rand() % 100;usleep(4321);char oper = opers[rand() % opers.size()];Request req(x, y, oper);req.DebugPrint();// 下面是根据协议发送给对方std::string package;req.Serialize(&package);                    // 序列化package = Encode(package);                  // 形成报文int fd = sockfd.Fd();                       // 获取套接字write(fd, package.c_str(), package.size()); // 将请求从客户端往服务端写过去// 下面是读取服务器发来的结果并解析char buffer[128];ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer)); // 读取服务器发回来的结果,但是这里也无法保证能读取到一个完整的报文if (n > 0)                                             // 读成功了{buffer[n] = 0;inbuffer_stream += buffer; // "len"\n"result code"\nstd::cout << inbuffer_stream << std::endl;std::string content;bool r = Decode(inbuffer_stream, &content); // 去掉报头"result code"\nassert(r);                                  // r为真说明报头成功去掉Response resp;r = resp.DeSerialize(content); // 对有效荷载进行反序列化assert(r);resp.DebugPrint(); // 打印结果}std::cout << "=================================================" << std::endl;sleep(1);cnt++;}sockfd.Close();return 0;
}

 效果演示:

http://www.rdtb.cn/news/21038.html

相关文章:

  • 中小企业品牌网站建设资源搜索器
  • 服务器网站日志实时新闻
  • 网站建设与管理感想网站建设策划书范文
  • 企业文化简介网站怎么做郴州网站建设网络推广渠道
  • 有什么做服装的网站吗如何制作一个个人网站
  • 票务网站官方客服做五休二b站入口2024已更新
  • 网站开发毕业论文怎么在百度上发布广告
  • oa系统有哪些功能seo网站介绍
  • 网站开发人员招聘广告语seo日常优化内容是什么
  • 徐州网站建设的特点拼多多代运营公司十大排名
  • 风铃网站代做新手怎么学做电商
  • 网站的设计传统营销与网络营销的区别
  • 网站建设dbd3营业推广策略有哪些
  • 百度网站建设百度seo自动优化
  • 重庆微信网站建设价格成都seo优化
  • 个人主体可以做网站吗云南疫情最新数据消息中高风险地区
  • 网站加网页网站大全软件下载
  • cms做网站不用后端市场推广方案怎么做
  • 手机网站格局苏州网站seo优化
  • 广州迅优网站建设公司广州seo网站优化培训
  • 怎么分析一个网站seo性能优化大师
  • 购物网站策划建设方案朋友圈网络营销
  • 做网站 写脚本是什么微信运营技巧
  • 一起做的网站如何做好市场推广
  • 济南做外贸的网站公司搜索引擎优化包括
  • 做外贸的都有哪些网站排名优化方法
  • 要怎么做网站推广无锡seo排名收费
  • 金华网站建设方案开发电商平台有哪些?
  • 如何让网站关键词搜录百度网盘客户端
  • 中国沈阳网站在哪里下载最新新闻事件