网络应用层协议的demo 运输层基于TCP
protocol.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#define SEP " "
#define SEP_LEN strlen(SEP)
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)
enum
{
OK = 0,
DIV_ZERO,
MOD_ZERO,
OP_ERROR
};
std::string enLength(const std::string &text)
{
std::string send_string = std::to_string(text.size());
send_string += LINE_SEP;
send_string += text;
send_string += LINE_SEP;
return send_string;
}
bool deLength(const std::string &package, std::string *text)
{
auto pos = package.find(LINE_SEP);
if (pos == std::string::npos)
return false;
std::string text_len_string = package.substr(0, pos);
int text_len = std::stoi(text_len_string);
*text = package.substr(pos + LINE_SEP_LEN, text_len);
return true;
}
class Request
{
public:
Request() : x(0), y(0), op(0)
{
}
Request(int x_, int y_, char op_) : x(x_), y(y_), op(op_)
{
}
bool serialize(std::string *out)
{
#ifdef MYSELF
*out = "";
// 结构化 -> "x op y";
std::string x_string = std::to_string(x);
std::string y_string = std::to_string(y);
*out = x_string;
*out += SEP;
*out += op;
*out += SEP;
*out += y_string;
#else
Json::Value root;
root["first"] = x;
root["second"] = y;
root["oper"] = op;
Json::FastWriter writer;
// Json::StyledWriter writer;
*out = writer.write(root);
#endif
return true;
}
bool deserialize(const std::string &in)
{
#ifdef MYSELF
// "x op y" -> 结构化
auto left = in.find(SEP);
auto right = in.rfind(SEP);
if (left == std::string::npos || right == std::string::npos)
return false;
if (left == right)
return false;
if (right - (left + SEP_LEN) != 1)
return false;
std::string x_string = in.substr(0, left);
std::string y_string = in.substr(right + SEP_LEN);
if (x_string.empty())
return false;
if (y_string.empty())
return false;
x = std::stoi(x_string);
y = std::stoi(y_string);
op = in[left + SEP_LEN];
#else
Json::Value root;
Json::Reader reader;
reader.parse(in, root);
x = root["first"].asInt();
y = root["second"].asInt();
op = root["oper"].asInt();
#endif
return true;
}
public:
int x;
int y;
char op;
};
class Response
{
public:
Response() : exitcode(0), result(0)
{
}
Response(int exitcode_, int result_) : exitcode(exitcode_), result(result_)
{
}
bool serialize(std::string *out)
{
#ifdef MYSELF
*out = "";
std::string ec_string = std::to_string(exitcode);
std::string res_string = std::to_string(result);
*out = ec_string;
*out += SEP;
*out += res_string;
#else
Json::Value root;
root["exitcode"] = exitcode;
root["result"] = result;
Json::FastWriter writer;
*out = writer.write(root);
#endif
return true;
}
bool deserialize(const std::string &in)
{
#ifdef MYSELF
// "exitcode result"
auto mid = in.find(SEP);
if (mid == std::string::npos)
return false;
std::string ec_string = in.substr(0, mid);
std::string res_string = in.substr(mid + SEP_LEN);
if (ec_string.empty() || res_string.empty())
return false;
exitcode = std::stoi(ec_string);
result = std::stoi(res_string);
#else
Json::Value root;
Json::Reader reader;
reader.parse(in, root);
exitcode = root["exitcode"].asInt();
result = root["result"].asInt();
#endif
return true;
}
public:
int exitcode; // 0:计算成功,!0表示计算失败
int result; // 计算结果
};
bool recvPackage(int sock, std::string &inbuffer, std::string *text)
{
char buffer[1024];
while (true)
{
ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (n > 0)
{
buffer[n] = 0;
inbuffer += buffer;
// 分析处理
auto pos = inbuffer.find(LINE_SEP);
if (pos == std::string::npos)
continue;
std::string text_len_string = inbuffer.substr(0, pos);
int text_len = std::stoi(text_len_string);
int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;
std::cout << "处理前#inbuffer: \n" << inbuffer << std::endl;
if (inbuffer.size() < total_len)
{
std::cout << "你输入的消息,没有严格遵守我们的协议,正在等待后续的内容, continue" << std::endl;
continue;
}
// 至少有一个完整的报文
*text = inbuffer.substr(0, total_len);
inbuffer.erase(0, total_len);
std::cout << "处理后#inbuffer:\n " << inbuffer << std::endl;
break;
}
else
return false;
}
return true;
}
calServer.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "log.hpp"
#include "protocol.hpp"
namespace server
{
enum
{
USAGE_ERR = 1,
SOCKET_ERR,
BIND_ERR,
LISTEN_ERR
};
static const uint16_t gport = 8080;
static const int gbacklog = 5;
typedef std::function func_t;
void handlerEntery(int sock, func_t func)
{
std::string inbuffer;
while (true)
{
std::string req_text, req_str;
if (!recvPackage(sock, inbuffer, &req_text))
return;
std::cout << "带报头的请求:\n" << req_text << std::endl;
if (!deLength(req_text, &req_str))
return;
std::cout << "去掉报头的正文:\n" << req_str << std::endl;
Request req;
if (!req.deserialize(req_str))
return;
Response resp;
func(req, resp);
std::string resp_str;
resp.serialize(&resp_str);
std::cout << "计算完成, 序列化响应: " << resp_str << std::endl;
std::string send_string = enLength(resp_str);
std::cout << "构建完成完整的响应\n" << send_string << std::endl;
send(sock, send_string.c_str(), send_string.size(), 0);
}
}
class CalServer
{
public:
CalServer(const uint16_t &port = gport) : _listensock(-1), _port(port)
{
}
void initServer()
{
// 1. 创建socket文件套接字对象
_listensock = socket(AF_INET, SOCK_STREAM, 0);
if (_listensock < 0)
{
exit(SOCKET_ERR);
}
// 2. bind绑定自己的网络信息
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(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
exit(BIND_ERR);
}
// 3. 设置socket 为监听状态
if (listen(_listensock, gbacklog) < 0)
{
exit(LISTEN_ERR);
}
}
void start(func_t func)
{
for (;;)
{
// 4. server 获取新链接
// sock, 和client进行通信的fd
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
if (sock < 0)
{
continue;
}
//多进程版
pid_t id = fork();
if (id == 0)
{
close(_listensock);
handlerEntery(sock, func);
close(sock);
exit(0);
}
close(sock);
pid_t ret = waitpid(id, nullptr, 0);
}
}
~CalServer() {}
private:
int _listensock;
uint16_t _port;
};
}
calServer.cc
#include "calServer.hpp"
#include
using namespace server;
using namespace std;
static void Usage(string proc)
{
cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
bool cal(const Request &req, Response &resp)
{
resp.exitcode = OK;
resp.result = OK;
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.exitcode = DIV_ZERO;
else
resp.result = req.x / req.y;
}
break;
case '%':
{
if (req.y == 0)
resp.exitcode = MOD_ZERO;
else
resp.result = req.x % req.y;
}
break;
default:
resp.exitcode = OP_ERROR;
break;
}
return true;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(USAGE_ERR);
}
uint16_t port = atoi(argv[1]);
unique_ptr tsvr(new CalServer(port));
tsvr->initServer();
tsvr->start(cal);
return 0;
}
calClient.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include "protocol.hpp"
#define NUM 1024
class CalClient
{
public:
CalClient(const std::string &serverip, const uint16_t &serverport)
: _sock(-1), _serverip(serverip), _serverport(serverport)
{
}
void initClient()
{
// 创建socket
_sock = socket(AF_INET, SOCK_STREAM, 0);
if (_sock < 0)
{
std::cerr << "socket create error" << std::endl;
exit(2);
}
}
void start()
{
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());
if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0)
{
std::cerr << "socket connect error" << std::endl;
}
else
{
std::string line;
std::string inbuffer;
while (true)
{
std::cout << "mycal>>> ";
std::getline(std::cin, line);
Request req = ParseLine(line);
std::string content;
req.serialize(&content);
std::string send_string = enLength(content);
std::cout << "sendstring:\n" << send_string << std::endl;
send(_sock, send_string.c_str(), send_string.size(), 0);
std::string package, text;
// "content_len"\r\n"exitcode result"\r\n
if (!recvPackage(_sock, inbuffer, &package))
continue;
if (!deLength(package, &text))
continue;
// "exitcode result"
Response resp;
resp.deserialize(text);
std::cout << "exitCode: " << resp.exitcode << std::endl;
std::cout << "result: " << resp.result << std::endl;
}
}
}
Request ParseLine(const std::string &line)
{
// 建议版本的状态机!
//"1+1" "123*456" "12/0"
int status = 0; // 0:操作符之前,1:碰到了操作符 2:操作符之后
int i = 0;
int cnt = line.size();
std::string left, right;
char op;
while (i < cnt)
{
switch (status)
{
case 0:
{
if(!isdigit(line[i]))
{
op = line[i];
status = 1;
}
else left.push_back(line[i++]);
}
break;
case 1:
i++;
status = 2;
break;
case 2:
right.push_back(line[i++]);
break;
}
}
std::cout << std::stoi(left)<<" " << std::stoi(right) << " " << op << std::endl;
return Request(std::stoi(left), std::stoi(right), op);
}
~CalClient()
{
if (_sock >= 0)
close(_sock);
}
private:
int _sock;
std::string _serverip;
uint16_t _serverport;
};
calClient.cc
#include "calClient.hpp"
#include
using namespace std;
static void Usage(string proc)
{
cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
string serverip = argv[1];
uint16_t serverport = atoi(argv[2]);
unique_ptr tcli(new CalClient(serverip, serverport));
tcli->initClient();
tcli->start();
return 0;
}