日志
将控制台上输出的信息利用任务线程写入到文件中,或自定义写入数据库等容器中
celllog.hpp
#ifndef _CELL_LOG_HPP_
#define _CELL_LOG_HPP_
#include"CELL.hpp"
#include"CELLTask.hpp"
#include<ctime>
class CELLLog
{
private:
CELLLog()
{
_taskServer.Start();
}
~CELLLog()
{
_taskServer.Close();
if (_logFile)
{
Info("CELLLog fclose(_logFile)\n");
fclose(_logFile);
_logFile = nullptr;
}
}
public:
static CELLLog& Instance() //单例模式
{
static CELLLog sLog;
return sLog;
}
void setLogPath(const char* logPath, const char* mode) //设置日志路径
{
if (_logFile)
{
Info("CELLLog::setLogPath _logFile != nullptr\n");
fclose(_logFile);
_logFile = nullptr;
}
_logFile = fopen(logPath, mode);
if (_logFile)
{
Info("CELLLog::setLogPath success,<%s,%s>\n", logPath, mode);
}
else {
Info("CELLLog::setLogPath failed,<%s,%s>\n", logPath, mode);
}
}
static void Info(const char* pStr) //输出日志
{
CELLLog* pLog = &Instance();
pLog->_taskServer.addTask([=]() {
if (pLog->_logFile)
{
auto t = system_clock::now();
auto tNow = system_clock::to_time_t(t);
//fprintf(pLog->_logFile, "%s", ctime(&tNow));
std::tm* now = std::gmtime(&tNow);
fprintf(pLog->_logFile, "%s", "Info ");
fprintf(pLog->_logFile, "[%d-%d-%d %d:%d:%d]", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
fprintf(pLog->_logFile, "%s", pStr);
fflush(pLog->_logFile);
}
printf("%s", pStr);
});
}
template<typename ...Args>
static void Info(const char* pformat, Args ... args) //按照格式输出日志
{
CELLLog* pLog = &Instance();
pLog->_taskServer.addTask([=]() {
if (pLog->_logFile)
{
auto t = system_clock::now();
auto tNow = system_clock::to_time_t(t);
//fprintf(pLog->_logFile, "%s", ctime(&tNow));
std::tm* now = std::gmtime(&tNow);
fprintf(pLog->_logFile, "%s", "Info ");
fprintf(pLog->_logFile, "[%d-%d-%d %d:%d:%d]", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
fprintf(pLog->_logFile, pformat, args...);
fflush(pLog->_logFile);
}
printf(pformat, args...);
});
}
private:
FILE* _logFile = nullptr;
CELLTaskServer _taskServer;
};
#endif
其它文件中的控制台输出都调用该日志类的输出
这次修改遇到了头文件循环引用问题,cell.h文件和其它.hpp文件可能需要修改
分离网络环境启动与关闭
可能需要启动多个easytcpserver,不能直接把网络环境的启动和关闭放在easytcpserver类中,那么创建多个easytcpserver时,会进行多次的启动和关闭
cellnetwork.cpp
#ifndef _CELL_NET_WORK_HPP_
#define _CELL_NET_WORK_HPP_
#include"CELL.hpp"
class CELLNetWork
{
private:
CELLNetWork()
{
#ifdef _WIN32
//启动Windows socket 2.x环境
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);
#endif
#ifndef _WIN32
//if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
// return (1);
//忽略异常信号,默认情况会导致进程终止
signal(SIGPIPE, SIG_IGN);
#endif
}
~CELLNetWork()
{
#ifdef _WIN32
//清除Windows socket环境
WSACleanup();
#endif
}
public:
static void Init()
{
static CELLNetWork obj; //单例模式,只会创建一个obj对象,同理只会进行一次析构
}
};
#endif // !_CELL_NET_WORK_HPP_
easytcpserver.hpp的修改
“`c++
#ifndef _EasyTcpServer_hpp_
#define _EasyTcpServer_hpp_
#include"CELL.hpp"
#include"CELLClient.hpp"
#include"CELLServer.hpp"
#include"INetEvent.hpp"
#include"CELLNetWork.hpp"
#include<thread>
#include<mutex>
#include<atomic>
class EasyTcpServer : public INetEvent
{
//初始化Socket
SOCKET InitSocket()
{
CELLNetWork::Init();
if (INVALID_SOCKET != _sock)
{
CELLLog::Info("warning, initSocket close old socket<%d>…\n", (int)_sock);
Close();
}
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET <span class="text-highlighted-inline" style="background-color: #fffd38;"> _sock)
{
CELLLog::Info("error, create socket failed…\n");
}
else {
CELLLog::Info("create socket<%d> success…\n", (int)_sock);
}
return _sock;
}</span>
<pre><code>//关闭Socket
void Close()
{
CELLLog::Info("EasyTcpServer.Close begin\n");
_thread.Close();
if (_sock != INVALID_SOCKET)
{
for (auto s : _cellServers)
{
delete s;
}
_cellServers.clear();
//关闭套节字socket
</code></pre>
#ifdef _WIN32
closesocket(_sock);
#else
close(_sock);
#endif
_sock = INVALID_SOCKET;
}
CELLLog::Info("EasyTcpServer.Close end\n");
}
};
#endif // !<em>EasyTcpServer_hpp</em>
“`