您的位置:首页 > 科技 > IT业 > 工业设计灵感网站_沈阳网下载_如何优化培训方式_百度快照手机版

工业设计灵感网站_沈阳网下载_如何优化培训方式_百度快照手机版

2025/7/14 10:00:22 来源:https://blog.csdn.net/qq_63580639/article/details/147019202  浏览:    关键词:工业设计灵感网站_沈阳网下载_如何优化培训方式_百度快照手机版
工业设计灵感网站_沈阳网下载_如何优化培训方式_百度快照手机版

应用层与协议

我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层.
协议是一种 “约定”. socket api 的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的. 如果我们要传输一些 “结构化的数据” 怎么办呢?
可以自定义协议!

网络版计算器(应用层)

例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端。
约定方案(大体框架):

struct request
{int x;//计算数1int y;//计算数2char op;//计算符号
}
struct response
{int result;//计算结果int code;//表示结果是否正确,正确为0等等
}

这就是一种约定。
在这里插入图片描述
假设两个人在聊天,发送的消息经过这个协议会形成字符串数据,这个就叫序列化,然后通过网络发送到对面手里,拿到之后把里面的字符串数据通过协议依次拿到手中。
同理,上面的计算器结构也是一样的。

x+y的时候计算符号两边规定添加一个空格。
\n作为一个有效字符串的结束符,这样就不会导致两个字符串合在一起无法区分的问题。
我们可以添加一个报头,报头里面装字符串的长度。(不算\n)
“9”\n"100 + 200"\n
如果是多个报文就是这种格式
“9”\n"100 + 200"\n“9”\n"100 + 200"\n“9”\n"100 + 200"\n

代码实现
打印模块

//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 lg;

计算协议

//Protocol.hpp
#pragma once
#include <iostream>
#include <string>using namespace std;const string blank_space_sep = " ";
const string protocol_sep = "\n";//有效字符串数据的分隔符,防止很多字符串数据叠加在一起难以分辨
string Encode(string &content)//添加字符串长度报文
{string package = to_string(content.size());package += protocol_sep;package += content;package += protocol_sep;return package;
}
// "len"\n"x op y"\nXXXXXX
// "protocolnumber"\n"len"\n"x op y"\nXXXXXX
bool Decode(string &package,string *content)
{size_t pos = package.find(protocol_sep);if(pos == string::npos) return false;string len_str = package.substr(0, pos);size_t len = stoi(len_str);// package = len_str + content_str + 2size_t total_len = len_str.size() + len + 2;if(package.size() < total_len) return false;//一段报文的整体长度*content = package.substr(pos+1, len);// earse 移除报文 package.erase(0, total_len);切割处理过的报文package.erase(0, total_len);return true;
}
class Request
{
public:Request(int data1,int data2,char oper):x(data1),y(data2),op(oper){}Request(){}
public:bool Serialize(string *out)//序列化{// 构建报文的有效载荷// struct => string, "x op y"string s = to_string(x);s += blank_space_sep;s += op;s += blank_space_sep;s += to_string(y);*out = s;return true;}bool Deserialize(const string &in)//反序列化{size_t left = in.find(blank_space_sep);if (left == string::npos)return false;string part_x = in.substr(0, left);size_t right = in.rfind(blank_space_sep);if (right == string::npos)return false;string part_y = in.substr(right + 1);if (left + 2 != right)return false;op = in[left + 1];x = stoi(part_x);y = stoi(part_y);return true;}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(){}
public:bool Serialize(string *out){// "result code"// 构建报文的有效载荷string s = to_string(result);s += blank_space_sep;s += to_string(code);*out = s;return true;}bool Deserialize(const std::string &in){std::size_t pos = in.find(blank_space_sep);if (pos == std::string::npos)return false;std::string part_left = in.substr(0, pos);std::string part_right = in.substr(pos+1);result = std::stoi(part_left);code = std::stoi(part_right);return true;}void DebugPrint(){std::cout << "结果响应完成, result: " << result << ", code: "<< code << std::endl;}
public:int result;int code;//0,可信,否则!0具体是几,表明对应错误的原因
};

创建套接字模块

//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"
using namespace std;
enum
{SocketErr = 2,BindErr,ListenErr,
};
const int backlog = 10;
class Sock
{
public:Sock(){}~Sock(){}void Socket(){sockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ < 0){lg(Fatal, "socker 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){lg(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}void Listen(){if (listen(sockfd_, backlog) < 0){lg(Fatal, "listen error, %s: %d", strerror(errno), errno);exit(ListenErr);}}int Accept(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){lg(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd;}bool Connect(const 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_;
};

TCP服务器

//TcpServer.hpp
#pragma once
#include <functional>
#include <string>
#include <signal.h>
#include "log.hpp"
#include "Socket.hpp"using func_t = function<string(string &package)>;
class TcpServer
{
public:TcpServer(uint16_t port, func_t callback) : port_(port), callback_(callback){}bool InitServer(){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_.Accept(&clientip,&clientport);if(sockfd < 0) continue;//提供服务lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);if(fork()==0){listensock_.Close();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;lg(Debug, "debug:\n%s", inbuffer_stream.c_str());while(true)//可以处理多个报文{string info = callback_(inbuffer_stream);if (info.empty()) break;//如果没有报文了就退出write(sockfd, info.c_str(), info.size());}}else if (n == 0)break;elsebreak;}exit(0);}close(sockfd);}}~TcpServer(){}
private:uint16_t port_;Sock listensock_;func_t callback_;
};

服务端

//ServerCal.hpp
#pragma once
#include <iostream>
#include "Protocol.hpp"enum
{Div_Zero = 1,Mod_Zero,Other_Oper
};class ServerCal
{
public: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;elseresp.result = req.x / req.y;}break;case '%':{if (req.y == 0)resp.code = Mod_Zero;elseresp.result = req.x % req.y;}break;default:resp.code = Other_Oper;break;}return resp;}// "len"\n"10 + 20"\nstring Calculator(string &package){string content;bool r = Decode(package, &content); // "len"\n"10 + 20"\nif (!r)return "";// "10 + 20"Request req;r = req.Deserialize(content); // "10 + 20" ->x=10 op=+ y=20if (!r)return "";content = "";                          //Response resp = CalculatorHelper(req); // result=30 code=0;resp.Serialize(&content);  // "30 0"content = Encode(content); // "len"\n"30 0"return content;}~ServerCal(){}
};
//ServerCal.cc
#include <unistd.h>
// #include "Daemon.hpp"static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl; 
}// ./servercal 8080
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();// Response resp(1000, 0);// std::string content;// resp.Serialize(&content);// std::cout << content << std::endl;// std::string package = Encode(content);// std::cout << package;// content = "";// bool r = Decode(package, &content);// std::cout << content << std::endl;// Response temp;// temp.Deserialize(content);// std::cout << temp.result << std::endl;// std::cout << temp.code << std::endl;// Request req(12364566, 43454356, '+');// std::string s;// req.Serialize(&s);// 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 << temp.x << std::endl;// std::cout << temp.op << std::endl;// std::cout << temp.y << std::endl;return 0;
}

客户端

//ClientCal.cc
#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;
}// ./clientcal ip port
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]);Sock sockfd;sockfd.Socket();bool r = sockfd.Connect(serverip, serverport);if(!r) return 1;srand(time(nullptr) ^ getpid());int cnt = 1;const std::string opers = "+-*/%=-=&^";std::string inbuffer_stream;while(cnt <= 10){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);write(sockfd.Fd(), package.c_str(), package.size());// std::cout << "这是最新的发出去的请求: " << n << "\n" << package;// n = write(sockfd.Fd(), package.c_str(), package.size());// std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;// n = write(sockfd.Fd(), package.c_str(), package.size());// std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;// n = write(sockfd.Fd(), package.c_str(), package.size());// std::cout << "这是最新的发出去的请求: \n" << n << "\n" << package;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"assert(r);Response resp;r = resp.Deserialize(content);assert(r);resp.DebugPrint();}std::cout << "=================================================" << std::endl;sleep(1);cnt++;}sockfd.Close();return 0;
}

json

JSON(JavaScript Object Notation,JavaScript对象表示法)是基于ECMAScript的一个子集设计的,是一种开放标准的文件格式和数据交换格式,它易于人阅读和编写,同时也易于机器解析和生成。JSON独立于语言设计,很多编程语言都支持JSON格式的数据交换。JSON是一种常用的数据格式,在电子数据交换中有多种用途,包括与服务器之间的Web应用程序的数据交换。其简洁和清晰的层次结构有效地提升了网络传输效率,使其成为理想的数据交换语言。其文件通常使用扩展名.json。(百度介绍)

安装json的命令:

sudo yum install epel-releaseyum install -y jq

安装之后库的名字是jsoncpp。

#include <iostream>
#include <jsoncpp/json/json.h>using namespace std;int main()
{Json::Value root;root["x"] = 100;root["y"] = 200;root["op"] = '+';root["dect"] = "this is a + oper";Json::FastWriter w;string res = w.write(root);cout << res <<endl;return 0;
}

在这里插入图片描述
打印出来的结果是按风格序列化之后的。

#include <iostream>
#include <jsoncpp/json/json.h>using namespace std;int main()
{Json::Value root;root["x"] = 100;root["y"] = 200;root["op"] = '+';root["dect"] = "this is a + oper";//Json::FastWriter w;Json::StyledWriter w;string res = w.write(root);cout << res <<endl;return 0;
}

在这里插入图片描述
这个是按风格增加可读性的序列化。

#include <iostream>
#include <jsoncpp/json/json.h>
#include <unistd.h>
using namespace std;int main()
{Json::Value root;//这里面能放任何类型的数据包括Jsonroot["x"] = 100;root["y"] = 200;root["op"] = '+';root["dect"] = "this is a + oper";Json::FastWriter w;//Json::StyledWriter w;string res = w.write(root);cout << res <<endl;Json::Value v;Json::Reader r;r.parse(res,v);//第一个参数是要反序列化的字符串,第二个参数是要放在哪里,第三个默认缺省int x = v["x"].asInt();//提取x的整数int y = v["y"].asInt();//提取y的整数char op = v["op"].asInt();//提取op的字符string dect = v["dect"].asString();//提取dect的字符串cout << x <<endl;cout << y <<endl;cout << op <<endl;cout << dect <<endl;return 0;
}

在这里插入图片描述
这是反序列化。

总结

上述代码说明,OSI七层中的会话层(决定什么时候连接,连接多久,连接什么时候断开),表示层(接收图像文字等信息),应用层(针对每个应用的协议),其实没办法在内核实现,刚刚实现的网络版本计算器说明这些都不是固定的,每个应用和用户需要是不一样的。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com