应用层是OSI模型或TCP/IP模型中的最高层,它直接为用户的应用程序提供网络服务。应用层的主要功能包括:
例如,我们需要实现一个服务器版的加法器。我们需要客户端把要计算的两个加数发过去,然后由服务器进行计算,最后再把结果返回给客户端。
约定方案一:
约定方案二:

无论我们采用方案一,还是方案二,还是其他的方案,只要保证,一端发送时构造的数据,在另一端能够正确的进行解析,就是OK的。这种约定,就是应用层协议。
但是,为了让我们深刻理解协议,我们打算自定义实现一下协议的过程。

代码结构
Calculate.hpp Makefile Socket.hpp TcpServer.hpp Daemon.hpp Protocol.hpp TcpClientMain.cc TcpServerMain.cc // 简单起见,可以直接采用自定义线程 // 建议不用用户输入,直接 client<<->>server 通信,这样可以省去编写没有干货的代码
Socket封装
socket.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define Convert(addrptr) ((struct sockaddr *)addrptr)
namespace Net_Work
{
const static int defaultsockfd = -1;
const int backlog = 5;
enum
{
SocketError = 1,
BindError,
ListenError,
};
// 封装一个基类,Socket 接口类
// 设计模式:模版方法类
class Socket
{
public:
virtual ~Socket() {}
virtual void CreateSocketOrDie() = 0;
virtual void BindSocketOrDie(uint16_t port) = 0;
virtual void ListenSocketOrDie(int backlog) = 0;
virtual Socket* AcceptConnection(std::string* peerip,
uint16_t* peerport) = 0;
virtual bool ConnectServer(std::string& serverip, uint16_t
serverport) = 0;
virtual int GetSockFd() = 0;
virtual void SetSockFd(int sockfd) = 0;
virtual void CloseSocket() = 0;
virtual bool Recv(std::string* buffer, int size) = 0;
virtual void Send(std::string& send_str) = 0;
// TODO
public:
void BuildListenSocketMethod(uint16_t port, int backlog)
{
CreateSocketOrDie();
BindSocketOrDie(port);
ListenSocketOrDie(backlog);
}
bool BuildConnectSocketMethod(std::string& serverip,
uint16_t serverport)
{
CreateSocketOrDie();
return ConnectServer(serverip, serverport);
}
void BuildNormalSocketMethod(int sockfd)
{
SetSockFd(sockfd);
}
};
class TcpSocket : public Socket
{
public:
TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
{
}
~TcpSocket()
{
}
void CreateSocketOrDie() override
{
_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (_sockfd < 0)
exit(SocketError);
}
void BindSocketOrDie(uint16_t port) override
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(port);
int n = ::bind(_sockfd, Convert(&local),
sizeof(local));
if (n < 0)
exit(BindError);
}
void ListenSocketOrDie(int backlog) override
{
int n = ::listen(_sockfd, backlog);
if (n < 0)
exit(ListenError);
}
Socket* AcceptConnection(std::string * peerip, uint16_t
* peerport) override
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int newsockfd = ::accept(_sockfd, Convert(&peer),
&len);
if (newsockfd < 0)
return nullptr;
*peerport = ntohs(peer.sin_port);
*peerip = inet_ntoa(peer.sin_addr);
Socket* s = new TcpSocket(newsockfd);
return s;
}
bool ConnectServer(std::string& serverip, uint16_t
serverport) override
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(serverip.c_str());
server.sin_port = htons(serverport);
int n = ::connect(_sockfd, Convert(&server),
sizeof(server));
if (n == 0)
return true;
else
return false;
}
int GetSockFd() override
{
return _sockfd;
}
void SetSockFd(int sockfd) override
{
_sockfd = sockfd;
}
void CloseSocket() override
{
if (_sockfd > defaultsockfd)
::close(_sockfd);
}
bool Recv(std::string* buffer, int size) override
{
char inbuffer[size];
ssize_t n = recv(_sockfd, inbuffer, size - 1, 0);
if (n > 0)
{
inbuffer[n] = 0;
*buffer += inbuffer; // 故意拼接的
return true;
}
else if (n == 0)
return false;
else
return false;
}
void Send(std::string& send_str) override
{
send(_sockfd, send_str.c_str(), send_str.size(), 0);
}
private:
int _sockfd;
};
}基本结构
定制基本的结构化字段,这个就是协议。
class Request
{
private:
// _data_x _oper _data_y
// 报文的自描述字段
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
// 很多工作都是在做字符串处理!
int _data_x; // 第一个参数
int _data_y; // 第二个参数
char _oper; // + - * / %
};
class Response
{
private:
// "len\r\n_result _code\r\n"
int _result; // 运算结果
int _code; // 运算状态
};protocol.hpp
#pragma once
#include <iostream>
#include <memory>
#include <jsoncpp/json/json.h>
namespace Protocol
{
// 问题
// 1. 结构化数据的序列和反序列化
// 2. 还要解决用户区分报文边界 --- 数据包粘报问题
// 讲法
// 1. 自定义协议
// 2. 成熟方案序列和反序列化
// "protocol_code\r\nlen\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
const std::string ProtSep = " ";
const std::string LineBreakSep = "\r\n";
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
std::string Encode(const std::string& message)
{
std::string len = std::to_string(message.size());
std::string package = len + LineBreakSep + message +
LineBreakSep;
return package;
}
// "len\nx op y\n" : \n 不属于报文的一部分,约定
// 我无法保证 package 就是一个独立的完整的报文
// "l
// "len
// "len\r\n
// "len\r\nx
// "len\r\nx op
// "len\r\nx op y
// "len\r\nx op y\r\n"
// "len\r\nx op y\r\n""len
// "len\r\nx op y\r\n""len\n
// "len\r\nx op
// "len\r\nx op y\r\n""len\nx op y\r\n"
// "len\r\nresult code\r\n""len\nresult code\r\n"
bool Decode(std::string &package, std::string *message)
{
// 除了解包,我还想判断报文的完整性, 能否正确处理具有"边界"的报文
auto pos = package.find(LineBreakSep);
if (pos == std::string::npos)
return false;
std::string lens = package.substr(0, pos);
int messagelen = std::stoi(lens);
int total = lens.size() + messagelen + 2 *
LineBreakSep.size();
if (package.size() < total)
return false;
// 至少 package 内部一定有一个完整的报文了!
*message = package.substr(pos + LineBreakSep.size(),
messagelen);
package.erase(0, total);
return true;
}
class Request
{
public:
Request() : _data_x(0), _data_y(0), _oper(0)
{
}
Request(int x, int y, char op) : _data_x(x), _data_y(y),
_oper(op)
{
}
void Debug()
{
std::cout << "_data_x: " << _data_x << std::endl;
std::cout << "_data_y: " << _data_y << std::endl;
std::cout << "_oper: " << _oper << std::endl;
}
void Inc()
{
_data_x++;
_data_y++;
}
// 结构化数据->字符串
bool Serialize(std::string* out)
{
Json::Value root;
root["datax"] = _data_x;
root["datay"] = _data_y;
root["oper"] = _oper;
Json::FastWriter writer;
*out = writer.write(root);
return true;
}
bool Deserialize(std::string & in) // "x op y" [)
{
Json::Value root;
Json::Reader reader;
bool res = reader.parse(in, root);
if (res)
{
_data_x = root["datax"].asInt();
_data_y = root["datay"].asInt();
_oper = root["oper"].asInt();
}
return res;
}
int GetX()
{
return _data_x;
}
int GetY()
{
return _data_y;
}
char GetOper()
{
return _oper;
}
private:
// _data_x _oper _data_y
// 报文的自描述字段
// "len\r\nx op y\r\n" : \r\n 不属于报文的一部分,约定
// 很多工作都是在做字符串处理!
int _data_x; // 第一个参数
int _data_y; // 第二个参数
char _oper; // + - * / %
};
class Response
{
public:
Response() : _result(0), _code(0)
{
}
Response(int result, int code) : _result(result),
_code(code)
{
}
bool Serialize(std::string * out)
{
Json::Value root;
root["result"] = _result;
root["code"] = _code;
Json::FastWriter writer;
*out = writer.write(root);
return true;
}
bool Deserialize(std::string & in) // "_result _code" [)
{
Json::Value root;
Json::Reader reader;
bool res = reader.parse(in, root);
if (res)
{
_result = root["result"].asInt();
_code = root["code"].asInt();
}
return res;
}
void SetResult(int res)
{
_result = res;
}
void SetCode(int code)
{
_code = code;
}
int GetResult()
{
return _result;
}
int GetCode()
{
return _code;
}
private:
// "len\r\n_result _code\r\n"
int _result; // 运算结果
int _code; // 运算状态
};
// 简单的工厂模式,建造类设计模式
class Factory
{
public:
std::shared_ptr<Request> BuildRequest()
{
std::shared_ptr<Request> req =
std::make_shared<Request>();
return req;
}
std::shared_ptr<Request> BuildRequest(int x, int y, char
op)
{
std::shared_ptr<Request> req =
std::make_shared<Request>(x, y, op);
return req;
}
std::shared_ptr<Response> BuildResponse()
{
std::shared_ptr<Response> resp =
std::make_shared<Response>();
return resp;
}
std::shared_ptr<Response> BuildResponse(int result, int
code)
{
std::shared_ptr<Response> req =
std::make_shared<Response>(result, code);
return req;
}
};
}期望的报文格式

const std::string ProtSep = " ";
const std::string LineBreakSep = "\n";
// "len\nx op y\n" : \n 不属于报文的一部分,约定
std::string Encode(const std::string& message)
{
std::string len = std::to_string(message.size());
std::string package = len + LineBreakSep + message +
LineBreakSep;
return package;
}
// "len\nx op y\n" : \n 不属于报文的一部分,约定
// 我无法保证 package 就是一个独立的完整的报文
// "l
// "len
// "len\n
// "len\nx
// "len\nx op
// "len\nx op y
// "len\nx op y\n"
// "len\nx op y\n""len
// "len\nx op y\n""len\n
// "len\nx op
// "len\nx op y\n""len\nx op y\n"
// "len\nresult code\n""len\nresult code\n"
bool Decode(std::string& package, std::string* message)
{
// 除了解包,我还想判断报文的完整性, 能否正确处理具有"边界"的报文
auto pos = package.find(LineBreakSep);
if (pos == std::string::npos)
return false;
std::string lens = package.substr(0, pos);
int messagelen = std::stoi(lens);
int total = lens.size() + messagelen + 2 *
LineBreakSep.size();
if (package.size() < total)
return false;
// 至少 package 内部一定有一个完整的报文了!
*message = package.substr(pos + LineBreakSep.size(),
messagelen);
package.erase(0, total);
return true;
}所以,完整的处理过程应该是:

感谢各位大佬支持!!!
互三啦!!!