潍坊市做网站,沈阳专业网站制作团队,WordPress为什么进不去,国内产品推广网站1、应用层
我们程序员写的一个个解决我们实际问题#xff0c;满足我们日常需求的网络程序#xff0c;都是在应用层。
2、再谈“协议” 协议是一种 约定 。 socket api 的接口 , 在读写数据时 , 都是按 字符串 的方式来发送接收的 . 如果我们… 1、应用层
我们程序员写的一个个解决我们实际问题满足我们日常需求的网络程序都是在应用层。
2、再谈“协议” 协议是一种 约定 。 socket api 的接口 , 在读写数据时 , 都是按 字符串 的方式来发送接收的 . 如果我们要传输一些 结构化的数据 怎么办呢 ? 这个时候就要进行序列化和反序列化什么意思呢如图 左边的客户端在给右边的服务器想发送这样一个msg的结构化的数据那么服务端就必须也要有一个完全相同的结构可以接受数据然后在客户端向网络发送数据时会先转换成字符串然后服务器接收时再将字符串转换成结构化的数据所以只要确保一端发送的数据另一端能够识别这种约定就是应用层的协议。 3、网络版计算器
Socket.hpp
这是一个封装的套接字接口可以创建套接字进行绑定、监听、接收、连接以及关闭套接字。
//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.hppenum{SocketErr 2,BindErr,ListenErr,
};//TODO
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, bind 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){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 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 std::endl;return false;}return true;}void Close(){close(sockfd_);}int Fd(){return sockfd_;}
private:int sockfd_;
};
TcpServer.hpp
下面是一个创建tcp服务器的接口实现了启动服务器的功能在里面调用其他接口处理收到的数据然后写回给客户端。
//TcpServer.hpp
#pragma once
#include functional
#include string
#include Log.hpp
#include Socket.hpp
#include signal.husing func_t std::functionstd::string (std::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){ std::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();std::string inbuffer_stream;// 数据计算while(true){char buffer[128];ssize_t n read(sockfd, buffer, sizeof(buffer));if (n 0){buffer[n] 0;inbuffer_stream buffer;lg(Debug, debug: %s, inbuffer_stream.c_str());std::string info callback_(inbuffer_stream);if(info.empty()) continue;write(sockfd, info.c_str(), info.size());}else if(n 0) break;else break;}exit(0);}close(sockfd);}
}
~TcpServer()
{}
private:uint16_t port_;Sock listensock_;func_t callback_;
};
ServerCal.hpp
这是计算器服务器的接口里面定义了计算器的基本功能加减乘除取模调用了Protocol接口中的序列化反序列化来将数据标准化。
//ServerCal.hpp
#pragma once
#include iostream
#include Protocol.hppenum{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;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;}// len\n10 20\nstd::string Calculator(std::string package){std::string content;bool r Decode(package,content); // len\n10 20\nif(!r) return ;// 10 20Request req;r req.Deserialize(content);// 10 20 - x10 op y20if(!r) return ;content ; //Response resp CalculatorHelper(req); resp.Serialize(content);content Encode(content);return content;}~ServerCal(){}
};
ServerCal.cc
这里是服务器启动的程序通过绑定端口号启动服务器。
//ServerCal.cc
#include TcpServer.hpp
#include ServerCal.hppstatic 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(8080, std::bind(ServerCal::Calculator, cal, std::placeholders::_1));tsvp-InitServer();tsvp-Start();return 0;
}
Protocol.hpp
这是对数据进行序列化反序列化的接口。
//Protocol.hpp
#pragma once #include iostream
#include string
#include jsoncpp/json/json.h// #define MySelf 1const std::string blank_space_sep ;
const std::string protocol_sep \n;std::string Encode(std::string content)
{// 封装报头std::string package std::to_string(content.size());package protocol_sep;package content;package protocol_sep;return package;
}//len\nx op y
bool Decode(std::string package, std::string *content)
{std::size_t pos package.find(protocol_sep);if (pos std::string::npos) return false;std::string len_str package.substr(0, pos);std::size_t len std::stoi(len_str);// package len_str content 2std::size_t total_len len_str.size() len 2;if(package.size() total_len) return false;*content package.substr(pos1, len);// earse 移除报文 package.erase(0, total_len);package.erase(0, total_len);return true;
}// json, protobuf
class Request
{
public:Request(int data1, int data2, char oper):x(data1), y(data2), op(oper){}Request(){}bool Serialize(std::string *out){
#ifdef MySelf// 构建报文的有效载荷// struct string,x op y len\nx op ystd::string s std::to_string(x);s blank_space_sep;s op;s blank_space_sep;s std::to_string(y);*out s;return true;
#else Json::Value root;root[x] x;root[y] y;root[op] op;//Json::FastWriter w;Json::StyledWriter w;*out w.write(root);return true;
#endif}bool Deserialize(const std::string in) // x op y{
#ifdef MySelfstd::size_t left in.find(blank_space_sep);if(left std::string::npos) return false;std::string part_x in.substr(0, left);std::size_t right in.rfind(blank_space_sep);if(right std::string::npos) return false;std::string part_y in.substr(right1);if(left 2 ! right) return false;op in[left 1];x std::stoi(part_x);y std::stoi(part_y);return true;
#else Json::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:// x op yint x;int y;char op; // - * / %
};class Response
{
public:Response(int res, int c):result(res), code(c){}Response(){}bool Serialize(std::string *out){
#ifdef MySelf// result code// 构建报文的有效载荷std::string s std::to_string(result);s blank_space_sep;s std::to_string(code);*out s;return true;
#else Json::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) // result cpde{
#ifdef MySelfstd::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(pos1);result std::stoi(part_left);code std::stoi(part_right);return true;
#else Json::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;int code; // 0, 可信否则0具体是几表明对应的错误原因
};
ClientCal.cc
这是客户端启动的程序通过服务器ip和端口连接服务器。
//ClientCal.cc
#include iostream
#include string
#include ctime
#include assert.h
#include unistd.h
#include Socket.hpp
#include Protocol.hppstatic 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);std::cout 这是最新的发出去的请求 \n package;write(sockfd.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\nresult code\nstd::string content;bool r Decode(inbuffer_stream, content); // result codeassert(r);Response resp;r resp.Deserialize(content);assert(r);resp.DebugPrint();}sleep(1);}sockfd.Close();return 0;
}
Log.hpp
这是日志接口。
//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.txtclass 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 logmessage(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\n, leftbuffer, rightbuffer);// // printf(%s, logtxt); // 暂时打印// printLog(level, logtxt);// }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.txtif (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/FatalprintOneFile(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;// int sum(int n, ...)
// {
// va_list s; // char*
// va_start(s, n);// int sum 0;
// while(n)
// {
// sum va_arg(s, int); // printf(hello %d, hello %s, hello %c, hello %d,, 1, hello, c, 123);
// n--;
// }// va_end(s); //s NULL
// return sum;
// }
Makefile
//Makefile
.PHONY:all
all:servercal clientcalFlag-DMySelf1
Lib-ljsoncppservercal:ServerCal.ccg -o $ $^ -stdc11 $(Lib) #$(Flag)
clientcal:ClientCal.ccg -o $ $^ -stdc11 -g $(Lib) #$(Flag).PHONY:clean
clean:rm -f servercal clientcal