Makefile
.PHONY:all
all:client server
client:CalClient.cc
g++ -o client CalClient.cc -std=c++11 -lpthread
server:CalServer.cc
g++ -o server CalServer.cc -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f client server
Log.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstdarg>
#include <cstdio>
#include <ctime>
#define DEBUG 0
#define NORMAL 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
const char *gLevelMap[5] = {"DEBUG", "NORMAL", "WARNING", "ERROR", "FATAL"};
#define LOGFILE "./threadpool.log"
void logMessage(int level, const char *format, ...)
{
char stdBuffer[1024]; // 标准部分
time_t timestamp = time(nullptr);
struct tm *time = localtime(×tamp);
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); // 使用 vsnprintf 而不是 vsprintf
va_end(args);
FILE* fp=fopen(LOGFILE,"a");
// 打印到显示器
// printf("%s %s\n", stdBuffer, logBuffer);
// 打印到指定文件
fprintf(fp,"%s%s\n",stdBuffer,logBuffer);
fclose(fp);
}
Protocol.hpp
#pragma once
#include <iostream>
#include <string>
#include <cstring>
namespace ns_protocal
{
#define MYSELF 1
#define SPACE " "
#define SPACE_LEN strlen(SPACE)
#define SEP "\r\n"
#define SEP_LEN strlen(SEP)
class Request
{
public:
std::string Serialize()
{
#ifdef MYSELF
std::string str;
str = std::to_string(_x);
str += SPACE;
str += _op;
str += SPACE;
str += std::to_string(_y);
return str;
#else
std::cout << "TODO" << std::endl;
#endif
}
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];
#else
std::cout << "TODO" << std::endl;
#endif
}
public:
Request(int x, int y, char op)
: _x(x), _y(y), _op(op)
{
}
Request() {}
Request(const Request ©)
{
_x = copy._x;
_y = copy._y;
_op = copy._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;
#endif
}
//"1 0"
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;
#endif
}
public:
Response(int result, int code)
: _result(result), _code(code)
{
}
Response() {}
~Response() {}
public:
int _result; // 计算结果
int _code; // 计算结果的状态码
};
bool Recv(int sock, std::string *out)
{
char buffer[1024];
ssize_t s = recv(sock, buffer, sizeof(buffer), 0);
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 << "send in" << std::endl;
int n = send(sock, str.c_str(), str.size(), 0);
if (n < 0) // 客户端关闭
std::cout << "send error" << std::endl;
}
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 "";
}
}
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;
}
}
Sock.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string>
#include <sys/wait.h>
#include "Log.hpp"
#include <memory>
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);
// local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
if (bind(sock, (const 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, "listen server success");
}
int Accept(int listensock, std::string *ip, uint16_t *port)
{
struct sockaddr_in src;
memset(&src, 0, sizeof(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()
{
}
};
TcpServer.hpp
#pragma once
#include "Sock.hpp"
#include <functional>
#include <pthread.h>
#include <vector>
#include <unordered_map>
#include <unistd.h>
namespace ns_tcpserver
{
using func_t = std::function<void(int)>;
class TcpServer;
class ThreadData
{
public:
ThreadData(int sock, TcpServer *server)
: _sock(sock), _server(server)
{
}
public:
int _sock;
TcpServer *_server;
};
class TcpServer
{
private:
static void *ThreadRoutine(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
td->_server->Excute(td->_sock);
close(td->_sock);
delete td;
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)
{
_funcs.push_back(func);
}
void Excute(int sock)
{
for (auto &f : _funcs)
{
f(sock);
}
}
void Start()
{
while (true)
{
std::string clientip;
uint16_t clientport;
int servicesock = _sock.Accept(_listensock, &clientip, &clientport);
if (servicesock == -1)
continue;
logMessage(NORMAL, "create new link success, sock: %d", servicesock);
pthread_t tid;
ThreadData *td = new ThreadData(servicesock, this);
pthread_create(&tid, nullptr, ThreadRoutine, td);
}
}
~TcpServer()
{
if (_listensock >= 0)
{
close(_listensock);
}
}
private:
int _listensock;
Sock _sock;
std::vector<func_t> _funcs;
// std::unordered_map<std::string, func_t> _func;
};
}
CalServer.cc
#include "TcpServer.hpp"
#include "Protocol.hpp"
#include <memory>
#include "Daemon.hpp"
using namespace ns_tcpserver;
using namespace ns_protocal;
void Usage(const char *args)
{
std::cout << "\nUsage: " << args << " port" << std::endl;
}
Response calculatorHelp(const Request &req);
void calculator(int sock)
{
std::string inbuffer;
while (true)
{
// 1.读取数据
bool res = Recv(sock, &inbuffer);
if (!res)
break;
// 读取成功
// 2.协议解析,保证得到一个完整的报文
std::string package = Decode(inbuffer);
if (package.empty())
continue;
// 3.保证该报文是一个完整的报文
logMessage(NORMAL, "%s", package.c_str());
Request req;
// 4.反序列化 字节流->结构化
req.Deserialized(package);
// 5.业务逻辑
Response resp = calculatorHelp(req);
// 序列化
std::string respString = resp.Serialize();
// 7.添加长度信息,形成一个完整的报文
//"length\r\ncode result\r\n
respString = Encode(respString);
Send(sock, respString);
}
}
Response calculatorHelp(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 = 1;
}
else
{
resp._result = req._x / req._y;
}
break;
case '%':
if (req._y == 0)
resp._code = 2;
else
resp._result = req._x % req._y;
break;
default:
resp._code = 3;
break;
}
return resp;
}
void handler(int signo)
{
std::cout << "get a signo: " << signo << std::endl;
exit(0);
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(1);
}
//signal(SIGPIPE, SIG_IGN);
std::unique_ptr<TcpServer> server(new TcpServer(atoi(argv[1])));
server->BindService(calculator);
MyDaemon();
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;
}
CalClient.cc
#include <iostream>
#include "Sock.hpp"
#include "Protocol.hpp"
#include <unistd.h>
using namespace ns_protocal;
void Usage(const char *args)
{
std::cout << "\nUsage: " << args << " serverIp serverPort" << std::endl;
}
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;
while (!quit)
{
// 1.获取需求
Request req(10, 20, '+');
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)
{
std::string buffer;
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 << temp << " =" << resp._result << "[success]" << std::endl;
break;
}
if (!err.empty())
std::cerr << err << std::endl;
break;
}
}
close(sockfd);
return 0;
}