#include
#include "Sock.hpp"
#include "Protocol.hpp"
using namespace ns_protocol;
static void Usage(const std::string &process)
{
std::cout << "\nUsage: " << process << " serverIp serverPort\n"
<< std::endl;
}
// ./client server_ip server_port
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string server_ip = argv[1];
uint16_t server_port = atoi(argv[2]);
Sock sock;
int sockfd = sock.Socket();
if (!sock.Connect(sockfd, server_ip, server_port))
{
std::cerr << "Connect error" << std::endl;
exit(2);
}
bool quit = false;
std::string buffer;
while (!quit)
{
// 1. 获取需求
Request req;
std::cout << "Please Enter # ";
std::cin >> req.x_ >> req.op_ >> req.y_;
// 2. 序列化
std::string s = req.Serialize();
// std::string temp = s;
// 3. 添加长度报头
s = Encode(s);
// 4. 发送给服务端
Send(sockfd, s);
// 5. 正常读取
while (true)
{
bool res = Recv(sockfd, &buffer);
if (!res)
{
quit = true;
break;
}
std::string package = Decode(buffer);
if (package.empty())
continue;
Response resp;
resp.Deserialized(package);
std::string err;
switch (resp.code_)
{
case 1:
err = "除0错误";
break;
case 2:
err = "模0错误";
break;
case 3:
err = "非法操作";
break;
default:
std::cout << resp.x_ << resp.op_ << resp.y_ << " = " << resp.result_ << " [success]" << std::endl;
break;
}
if(!err.empty()) std::cerr << err << std::endl;
// sleep(1);
break;
}
}
close(sockfd);
return 0;
}
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include "Daemon.hpp"
#include
#include
using namespace ns_tcpserver;
using namespace ns_protocol;
static void Usage(const std::string &process)
{
std::cout << "\nUsage: " << process << " port\n"
<< std::endl;
}
static Response calculatorHelper(const Request &req)
{
Response resp(0, 0, req.x_, req.y_, req.op_);
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 (0 == req.y_)
resp.code_ = 1;
else
resp.result_ = req.x_ / req.y_;
break;
case '%':
if (0 == req.y_)
resp.code_ = 2;
else
resp.result_ = req.x_ % req.y_;
break;
default:
resp.code_ = 3;
break;
}
return resp;
}
void calculator(int sock)
{
std::string inbuffer;
while (true)
{
// 1. 读取成功
bool res = Recv(sock, &inbuffer); // 在这里我们读到了一个请求?
if (!res)
break;
// std::cout << "begin: inbuffer: " << inbuffer << std::endl;
// 2. 协议解析,保证得到一个完整的报文
std::string package = Decode(inbuffer);
if (package.empty())
continue;
// std::cout << "end: inbuffer: " << inbuffer << std::endl;
// std::cout << "packge: " << package << std::endl;
logMessage(NORMAL, "%s", package.c_str());
// 3. 保证该报文是一个完整的报文
Request req;
// 4. 反序列化,字节流 -> 结构化
req.Deserialized(package); // 反序列化
// 5. 业务逻辑
Response resp = calculatorHelper(req);
// 6. 序列化
std::string respString = resp.Serialize(); // 对计算结果进行序列化
// 7. 添加长度信息,形成一个完整的报文
// "length\r\ncode result\r\n"
// std::cout << "respString: " << respString << std::endl;
respString = Encode(respString);
// std::cout << "encode: respString: " << respString << std::endl;
// 8. send这里我们暂时先这样写,多路转接的时候,我们再来谈发送的问题
Send(sock, respString);
}
}
// ./CalServer port
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
// 一般经验:server在编写的时候,要有较为严谨性的判断逻辑
// 一般服务器,都是要忽略SIGPIPE信号的,防止在运行中出现非法写入的问题!
// signal(SIGPIPE, SIG_IGN);
MyDaemon();
std::unique_ptr server(new TcpServer(atoi(argv[1])));
server->BindService(calculator);
server->Start();
// Request req(123, 456, '+');
// std::string s = req.Serialize();
// std::cout << s << std::endl;
// Request temp;
// temp.Deserialized(s);
// std::cout << temp.x_ << std::endl;
// std::cout << temp.op_ << std::endl;
// std::cout << temp.y_ << std::endl;
return 0;
}
#pragma once
#include
#include
#include
#include
#include
#include
// 设置守护进程
void MyDaemon()
{
// 1. 忽略信号,SIGPIPE,SIGCHLD
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
// 2. 不要让自己成为组长
if (fork() > 0)
exit(0);
// 3. 调用setsid
setsid();
// 4. 标准输入,标准输出,标准错误的重定向,守护进程不能直接向显示器打印消息
int devnull = open("/dev/null", O_RDONLY | O_WRONLY);
if(devnull > 0)
{
dup2(0, devnull);
dup2(1, devnull);
dup2(2, devnull);
close(devnull);
}
}
#pragma once
#include
#include
#include
#include
#include
// 日志是有日志级别的
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
const char *gLevelMap[] = {
"DEBUG",
"NORMAL",
"WARNING",
"ERROR",
"FATAL"
};
#define LOGFILE "./calculator.log"
// 完整的日志功能,至少: 日志等级 时间 支持用户自定义(日志内容, 文件行,文件名)
void logMessage(int level, const char *format, ...)
{
#ifndef DEBUG_SHOW
if(level== DEBUG) return;
#endif
char stdBuffer[1024]; //标准部分
time_t timestamp = time(nullptr);
snprintf(stdBuffer, sizeof stdBuffer, "[%s] [%ld] ", gLevelMap[level], timestamp);
char logBuffer[1024]; //自定义部分
va_list args;
va_start(args, format);
vsnprintf(logBuffer, sizeof logBuffer, format, args);
va_end(args);
FILE *fp = fopen(LOGFILE, "a");
fprintf(fp, "%s%s\n", stdBuffer, logBuffer);
fclose(fp);
}
.PHONY:all
all:client CalServer
client:CalClient.cc
g++ -o $@ $^ -std=c++11
CalServer:CalServer.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f client CalServer
#pragma once
#include
#include
#include
// #include
namespace ns_protocol
{
#define MYSELF 0
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define SEP "\r\n"
#define SEP_LEN strlen(SEP) // 不能是sizeof!
class Request
{
public:
// 1. 自主实现 "length\r\nx_ op_ y_\r\n"
// 2. 使用现成的方案
// 序列化
std::string Serialize()
{
#ifdef MYSELF
// 使用自己定义的方式
std::string str;
str = std::to_string(x_);
str += SPACE;
str += op_; // TODO
str += SPACE;
str += std::to_string(y_);
return str;
#else
Json::Value root;
root["x"] = x_;
root["y"] = y_;
root["op"] = op_;
Json::FastWriter writer;
return writer.write(root);
#endif
}
// "x_ op_ y_"
// "1234 + 5678"
// 反序列化
bool Deserialized(const std::string &str)
{
#ifdef MYSELF
std::size_t left = str.find(SPACE);
if (left == std::string::npos)
return false;
std::size_t right = str.rfind(SPACE);
if (right == std::string::npos)
return false;
x_ = atoi(str.substr(0, left).c_str());
y_ = atoi(str.substr(right + SPACE_LEN).c_str());
if (left + SPACE_LEN > str.size())
return false;
else
op_ = str[left + SPACE_LEN];
return true;
#else
Json::Value root;
Json::Reader reader;
reader.parse(str, root);
x_ = root["x"].asInt();
y_ = root["y"].asInt();
op_ = root["op"].asInt();
return true;
#endif
}
public:
Request()
{
}
Request(int x, int y, char op) : x_(x), y_(y), op_(op)
{
}
~Request() {}
public:
int x_; // 是什么?
int y_; // 是什么?
char op_; // '+' '-' '*' '/' '%'
};
class Response
{
public:
// "code_ result_"
// 结果序列化
std::string Serialize()
{
#ifdef MYSELF
std::string s;
s = std::to_string(code_);
s += SPACE;
s += std::to_string(result_);
return s;
#else
Json::Value root;
root["code"] = code_;
root["result"] = result_;
root["xx"] = x_;
root["yy"] = y_;
root["zz"] = op_;
Json::FastWriter writer;
return writer.write(root);
#endif
}
// "111 100"
// 结果反序列化
bool Deserialized(const std::string &s)
{
#ifdef MYSELF
std::size_t pos = s.find(SPACE);
if (pos == std::string::npos)
return false;
code_ = atoi(s.substr(0, pos).c_str());
result_ = atoi(s.substr(pos + SPACE_LEN).c_str());
return true;
#else
Json::Value root;
Json::Reader reader;
reader.parse(s, root);
code_ = root["code"].asInt();
result_ = root["result"].asInt();
x_ = root["xx"].asInt();
y_ = root["yy"].asInt();
op_ = root["zz"].asInt();
return true;
#endif
}
public:
Response()
{
}
Response(int result, int code, int x, int y, char op)
: result_(result), code_(code), x_(x), y_(y), op_(op)
{
}
~Response() {}
public:
int result_; // 计算结果
int code_; // 计算结果的状态码
int x_;
int y_;
char op_;
};
// 临时方案
// 调整方案2: 我们期望,你必须给我返回一个完整的报文
bool Recv(int sock, std::string *out)
{
// UDP是面向数据报:
// TCP 面向字节流的:
// recv : 你怎么保证,你读到的inbuffer,是一个完整完善的请求呢?不能保证
// "1234 + 5678" : 1234 +
// "1234 + 5678" : 1234 + 5678 123+99
// "1234 "
// 必须是:"1234 + 5678"
// 单纯的recv是无法解决这个问题的,需要对协议进一步定制!
char buffer[1024];
ssize_t s = recv(sock, buffer, sizeof(buffer)-1, 0); // 9\r\n123+789\r\n
if (s > 0)
{
buffer[s] = 0;
*out += buffer;
}
else if (s == 0)
{
// std::cout << "client quit" << std::endl;
return false;
}
else
{
// std::cout << "recv error" << std::endl;
return false;
}
return true;
}
void Send(int sock, const std::string str)
{
// std::cout << "sent in" << std::endl;
int n = send(sock, str.c_str(), str.size(), 0);
if (n < 0)
std::cout << "send error" << std::endl;
}
// "length\r\nx_ op_ y_\r\n..." // 10\r\nabc
// "x_ op_ y_\r\n length\r\nXXX\r\n"
// 反序列化(进一步)
std::string Decode(std::string &buffer)
{
std::size_t pos = buffer.find(SEP);
if(pos == std::string::npos) return "";
int size = atoi(buffer.substr(0, pos).c_str());
int surplus = buffer.size() - pos - 2*SEP_LEN;
if(surplus >= size)
{
//至少具有一个合法完整的报文, 可以动手提取了
buffer.erase(0, pos+SEP_LEN);
std::string s = buffer.substr(0, size);
buffer.erase(0, size + SEP_LEN);
return s;
}
else
{
return "";
}
}
// "XXXXXX"
// "123\r\nXXXXXX\r\n"
// 序列化(进一步)
std::string Encode(std::string &s)
{
std::string new_package = std::to_string(s.size());
new_package += SEP;
new_package += s;
new_package += SEP;
return new_package;
}
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Log.hpp"
// 封装一下套接字 TCP
class Sock
{
private:
const static int gbacklog = 20;
public:
Sock() {}
int Socket()
{
int listensock = socket(AF_INET, SOCK_STREAM, 0);// 监听套接字
if (listensock < 0)
{
logMessage(FATAL, "create socket error, %d:%s", errno, strerror(errno));
exit(2);
}
logMessage(NORMAL, "create socket success, listensock: %d", listensock);
return listensock;
}
void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0")
{
struct sockaddr_in local;
memset(&local, 0, sizeof local);
local.sin_family = AF_INET;
local.sin_port = htons(port);// 端口转换
inet_pton(AF_INET, ip.c_str(), &local.sin_addr);// 端口转换
if (bind(sock, (struct sockaddr *)&local, sizeof(local)) < 0)
{
logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
exit(3);
}
}
void Listen(int sock)
{
if (listen(sock, gbacklog) < 0)
{
logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
exit(4);
}
logMessage(NORMAL, "init server success");
}
// 一般经验
// const std::string &: 输入型参数
// std::string *: 输出型参数
// std::string &: 输入输出型参数
int Accept(int listensock, std::string *ip, uint16_t *port)
{
struct sockaddr_in src;
socklen_t len = sizeof(src);
int servicesock = accept(listensock, (struct sockaddr *)&src, &len);// 转移
if (servicesock < 0)
{
logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
return -1;
}
if(port) *port = ntohs(src.sin_port);
if(ip) *ip = inet_ntoa(src.sin_addr);
return servicesock;
}
bool Connect(int sock, const std::string &server_ip, const uint16_t &server_port)
{
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(server_port);
server.sin_addr.s_addr = inet_addr(server_ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) == 0) return true;
else return false;
}
~Sock() {}
};
#pragma once
#include "Sock.hpp"
#include
#include
#include
namespace ns_tcpserver
{
using func_t = std::function;// 包装器 - 包装的是一个函数
class TcpServer;
// 这个更像是一个结构体
class ThreadData
{
public:
ThreadData(int sock, TcpServer *server):sock_(sock), server_(server)
{}
~ThreadData() {}
public:
int sock_;
TcpServer *server_;
};
class TcpServer
{
private:
static void *ThreadRoutine(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast(args);
td->server_->Excute(td->sock_);
close(td->sock_);
return nullptr;
}
public:
TcpServer(const uint16_t &port, const std::string &ip = "0.0.0.0")
{
// 初始化
listensock_ = sock_.Socket();
sock_.Bind(listensock_, port, ip);
sock_.Listen(listensock_);
}
// 新增
void BindService(func_t func)
{
func_.push_back(func);
}
// 执行
void Excute(int sock)
{
for(auto &f : func_)
{
f(sock);
}
}
void Start()
{
for (;;)
{
std::string clientip;
uint16_t clientport;
int sock = sock_.Accept(listensock_, &clientip, &clientport);
if (sock == -1)
continue;
logMessage(NORMAL, "create new link success, sock: %d", sock);
// 创建进程
pthread_t tid;
ThreadData *td = new ThreadData(sock, this);
pthread_create(&tid, nullptr, ThreadRoutine, td);
}
}
~TcpServer()
{
if (listensock_ >= 0)
close(listensock_);// 就像关闭文件描述符一样
}
private:
int listensock_;
Sock sock_;
std::vector func_;
};
}
全都是在前台运行的