Merge branch 'main' of https://git.happytanuki.kr/HappyTanuki/NP_Midterm
This commit is contained in:
@@ -15,7 +15,7 @@ int main() {
|
||||
spdlog::info("Connection established from {}", (std::string)serveraddr);
|
||||
|
||||
Chattr::DataPostPacket dataPostPacket;
|
||||
dataPostPacket.__data.packetType = Chattr::PacketType::PACKET_POST;
|
||||
dataPostPacket.__data.packetType = Chattr::PacketCategory::PACKET_POST;
|
||||
dataPostPacket.__data.requestType = Chattr::RequestType::DATA;
|
||||
dataPostPacket.__data.dataType = Chattr::DataType::TEXT;
|
||||
dataPostPacket.__data.packetLength = 14;
|
||||
|
||||
@@ -1,11 +1,81 @@
|
||||
#pragma once
|
||||
#include "Utils/ThreadPool.hpp"
|
||||
#include "Utils/ConfigManager.hpp"
|
||||
#include "Socket/IOCP.hpp"
|
||||
#include "Packet/Packet.hpp"
|
||||
|
||||
namespace Chattr {
|
||||
|
||||
class ServerManager {
|
||||
public:
|
||||
void init();
|
||||
void
|
||||
void _IOCPClient(ThreadPool* thread, IOCPPASSINDATA* data);
|
||||
|
||||
PacketSet packetParser(Packet Packet);
|
||||
|
||||
template<typename _Callable>
|
||||
void init(_Callable _IOCPClient) {
|
||||
auto config = ConfigManager::load();
|
||||
log::setDefaultLogger(config.logLevel, config.logFileName, config.logfileSize, config.logfileCount);
|
||||
threadPool_.init(0);
|
||||
iocp_.init(&threadPool_, _IOCPClient);
|
||||
|
||||
struct Address serveraddr;
|
||||
if (config.ipVersion == 4) {
|
||||
listenSock_.init(AF_INET);
|
||||
serveraddr.set(AF_INET, INADDR_ANY, config.listenPort);
|
||||
clientAddr_.setType(AF_INET);
|
||||
}
|
||||
else if (config.ipVersion == 6) {
|
||||
listenSock_.init(AF_INET6);
|
||||
serveraddr.set(AF_INET6, in6addr_any, config.listenPort);
|
||||
clientAddr_.setType(AF_INET6);
|
||||
}
|
||||
listenSock_.bind(serveraddr);
|
||||
listenSock_.listen(SOMAXCONN);
|
||||
}
|
||||
|
||||
void init() {
|
||||
init([this](ThreadPool* thread, IOCPPASSINDATA* data) {
|
||||
_IOCPClient(thread, data);
|
||||
});
|
||||
}
|
||||
|
||||
void registerUser(std::string userName, std::shared_ptr<TCPSocket> sock);
|
||||
void deleteUser(Snowflake UID);
|
||||
std::vector<std::pair<Snowflake, std::string>> getUserList();
|
||||
|
||||
void createRoom(std::string roomName);
|
||||
void deleteRoom(Snowflake RID);
|
||||
std::vector<std::pair<Snowflake, std::string>> getRoomList();
|
||||
|
||||
void joinRoom(Snowflake UID, Snowflake RID);
|
||||
void exitRoom(Snowflake UID, Snowflake RID);
|
||||
|
||||
Snowflake generateID();
|
||||
|
||||
void run();
|
||||
private:
|
||||
ThreadPool threadPool_;
|
||||
IOCP iocp_;
|
||||
|
||||
TCPSocket listenSock_;
|
||||
|
||||
TCPSocket clientSock_;
|
||||
struct Address clientAddr_;
|
||||
|
||||
std::mutex resourceMutex_;
|
||||
std::mutex snowflakeGenerateMutex_;
|
||||
|
||||
std::unordered_map<Snowflake, std::string> roomNames_;
|
||||
|
||||
std::unordered_map<Snowflake, std::unordered_map<Snowflake, std::shared_ptr<TCPSocket>>> rooms_;
|
||||
|
||||
std::unordered_map<Snowflake, std::shared_ptr<TCPSocket>> UID2userSocket_;
|
||||
std::unordered_map<TCPSocket*, Snowflake> userSocket2UID_;
|
||||
|
||||
std::unordered_map<Snowflake, std::string> userNames_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
#include "ServerManager/ServerManager.hpp"
|
||||
#include "Utils/ConfigManager.hpp"
|
||||
#include "Socket/Log.hpp"
|
||||
|
||||
namespace Chattr {
|
||||
|
||||
void ServerManager::_IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDATA* data) {
|
||||
Chattr::Packet pack;
|
||||
int packetSize = data->transfrredbytes;
|
||||
|
||||
if (data->recvbytes == 0) {
|
||||
data->recvbytes = data->transfrredbytes;
|
||||
data->transfrredbytes = 0;
|
||||
}
|
||||
else if (data->transfrredbytes <= data->sendbytes) {
|
||||
data->IOCPInstance->recv(data, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(pack.serialized, data->wsabuf.buf, data->wsabuf.len);
|
||||
pack.convToH();
|
||||
|
||||
std::string recvString;
|
||||
bool packetError = false;
|
||||
|
||||
DataPostPacket dataPostPacket;
|
||||
ResponsePacket responsePacket;
|
||||
LoginRequestPacket loginRequestPacket;
|
||||
|
||||
switch (packetParser(pack)) {
|
||||
case PacketSet::LOGINREQUEST:
|
||||
break;
|
||||
case PacketSet::ROOMCREATEREQUEST:
|
||||
break;
|
||||
case PacketSet::ROOMLISTREQUEST:
|
||||
break;
|
||||
case PacketSet::ROOMJOINREQUEST:
|
||||
break;
|
||||
case PacketSet::ROOMEXITREQUEST:
|
||||
break;
|
||||
case PacketSet::USERSLISTREQUEST:
|
||||
break;
|
||||
case PacketSet::DATAPOSTTEXT:
|
||||
std::memcpy(&dataPostPacket.serialized, &pack, 1500);
|
||||
dataPostPacket.__data.packetLength = (dataPostPacket.__data.packetLength > 1487) ? 1487 : dataPostPacket.__data.packetLength;
|
||||
recvString = std::string((char*)dataPostPacket.__data.data, dataPostPacket.__data.packetLength - (sizeof(std::uint16_t) * 4));
|
||||
spdlog::info("Received [{}] from : [{}]", recvString, (std::string)data->socket->remoteAddr);
|
||||
|
||||
responsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||
responsePacket.__data.requestType = Chattr::RequestType::DATA;
|
||||
responsePacket.__data.dataType = Chattr::DataType::TEXT;
|
||||
responsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
responsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
responsePacket.convToN();
|
||||
memcpy(data->wsabuf.buf, responsePacket.serialized, responsePacket.__data.packetLength + 8);
|
||||
|
||||
// data->sendbytes = responsePacket.__data.packetLength + 5;
|
||||
data->sendbytes = 1500;
|
||||
data->IOCPInstance->send(data, 1, 0);
|
||||
break;
|
||||
case PacketSet::DATAPOSTBINARY:
|
||||
break;
|
||||
case PacketSet::CONTINUE:
|
||||
break;
|
||||
case PacketSet::INVALID:
|
||||
default:
|
||||
responsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||
responsePacket.__data.requestType = Chattr::RequestType::DATA;
|
||||
responsePacket.__data.dataType = Chattr::DataType::TEXT;
|
||||
responsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
responsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
responsePacket.convToN();
|
||||
memcpy(data->wsabuf.buf, responsePacket.serialized, responsePacket.__data.packetLength + 8);
|
||||
// data->sendbytes = responsePacket.__data.packetLength + 5;
|
||||
data->sendbytes = responsePacket.__data.packetLength + 8;
|
||||
data->transfrredbytes = responsePacket.__data.packetLength + 8;
|
||||
data->IOCPInstance->send(data, 1, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PacketSet ServerManager::packetParser(Packet Packet) {
|
||||
if (Packet.__data.packetLength < 0 || Packet.__data.packetLength > 1500)
|
||||
return PacketSet::INVALID;
|
||||
|
||||
switch (Packet.__data.packetType) {
|
||||
case PacketCategory::PACKET_POST:
|
||||
if (Packet.__data.requestType != RequestType::DATA) {
|
||||
return PacketSet::INVALID;
|
||||
}
|
||||
switch (Packet.__data.dataType) {
|
||||
case DataType::TEXT:
|
||||
return PacketSet::DATAPOSTTEXT;
|
||||
case DataType::BINARY:
|
||||
return PacketSet::DATAPOSTBINARY;
|
||||
default:
|
||||
return PacketSet::INVALID;
|
||||
}
|
||||
break;
|
||||
case PacketCategory::PACKET_REQUEST:
|
||||
switch (Packet.__data.requestType) {
|
||||
case RequestType::LOGIN:
|
||||
return PacketSet::LOGINREQUEST;
|
||||
case RequestType::ROOM_CREATE:
|
||||
return PacketSet::ROOMCREATEREQUEST;
|
||||
case RequestType::ROOM_LIST:
|
||||
return PacketSet::ROOMLISTREQUEST;
|
||||
case RequestType::ROOM_JOIN:
|
||||
return PacketSet::ROOMJOINREQUEST;
|
||||
case RequestType::ROOM_EXIT:
|
||||
return PacketSet::ROOMEXITREQUEST;
|
||||
case RequestType::USERS_LIST:
|
||||
return PacketSet::USERSLISTREQUEST;
|
||||
case RequestType::DATA:
|
||||
return PacketSet::INVALID;
|
||||
}
|
||||
break;
|
||||
case PacketCategory::PACKET_RESPONSE:
|
||||
switch (Packet.__data.requestType) {
|
||||
case RequestType::LOGIN:
|
||||
return PacketSet::LOGINRESPONSE;
|
||||
case RequestType::ROOM_CREATE:
|
||||
return PacketSet::ROOMCREATERESPONSE;
|
||||
case RequestType::ROOM_LIST:
|
||||
return PacketSet::ROOMLISTRESPONSE;
|
||||
case RequestType::ROOM_JOIN:
|
||||
return PacketSet::ROOMJOINRESPONSE;
|
||||
case RequestType::ROOM_EXIT:
|
||||
return PacketSet::ROOMEXITRESPONSE;
|
||||
case RequestType::USERS_LIST:
|
||||
return PacketSet::USERSLISTRESPONSE;
|
||||
case RequestType::DATA:
|
||||
return PacketSet::INVALID;
|
||||
}
|
||||
break;
|
||||
case PacketCategory::PACKET_CONTINUE:
|
||||
return PacketSet::CONTINUE;
|
||||
default:
|
||||
return PacketSet::INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerManager::registerUser(std::string userName, std::shared_ptr<TCPSocket> sock) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
Snowflake UID = generateID();
|
||||
userNames_[UID] = userName;
|
||||
UID2userSocket_[UID] = sock;
|
||||
userSocket2UID_[sock.get()] = UID;
|
||||
}
|
||||
|
||||
void ServerManager::deleteUser(Snowflake UID) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
userNames_.erase(UID);
|
||||
std::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
|
||||
UID2userSocket_.erase(UID);
|
||||
userSocket2UID_.erase(sock.get());
|
||||
}
|
||||
|
||||
std::vector<std::pair<Snowflake, std::string>> ServerManager::getUserList() {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
std::vector<std::pair<Snowflake, std::string>> userList;
|
||||
userList.reserve(userNames_.size());
|
||||
|
||||
for (auto user : userNames_)
|
||||
userList.push_back(user);
|
||||
|
||||
return userList;
|
||||
}
|
||||
|
||||
void ServerManager::createRoom(std::string roomName) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
Snowflake RID = generateID();
|
||||
roomNames_[RID] = roomName;
|
||||
rooms_[RID] = std::unordered_map<Snowflake, std::shared_ptr<TCPSocket>>();
|
||||
}
|
||||
|
||||
void ServerManager::deleteRoom(Snowflake RID) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
roomNames_.erase(RID);
|
||||
rooms_.erase(RID);
|
||||
}
|
||||
|
||||
std::vector<std::pair<Snowflake, std::string>> ServerManager::getRoomList() {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
std::vector<std::pair<Snowflake, std::string>> roomList;
|
||||
roomList.reserve(roomNames_.size());
|
||||
|
||||
for (auto user : roomNames_)
|
||||
roomList.push_back(user);
|
||||
|
||||
return roomList;
|
||||
}
|
||||
|
||||
void ServerManager::joinRoom(Snowflake UID, Snowflake RID) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
std::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
|
||||
rooms_[RID][UID] = sock;
|
||||
}
|
||||
|
||||
void ServerManager::exitRoom(Snowflake UID, Snowflake RID) {
|
||||
std::lock_guard<std::mutex> lock(resourceMutex_);
|
||||
rooms_[RID].erase(UID);
|
||||
}
|
||||
|
||||
static struct _EPOCH {
|
||||
_EPOCH() {
|
||||
EPOCH = std::chrono::system_clock::now();
|
||||
}
|
||||
std::chrono::system_clock::time_point EPOCH;
|
||||
} __EPOCH__;
|
||||
|
||||
Snowflake ServerManager::generateID() {
|
||||
std::lock_guard<std::mutex> lock(snowflakeGenerateMutex_);
|
||||
#ifdef _WIN32
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
#elif __linux__
|
||||
pthread_t tid = pthread_self();
|
||||
#endif
|
||||
static int sequence = 0;
|
||||
Snowflake id = {};
|
||||
|
||||
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - __EPOCH__.EPOCH);
|
||||
id.timestamp = timestamp.count();
|
||||
id.instance = tid;
|
||||
id.sequence = sequence++;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ServerManager::run() {
|
||||
while (true) {
|
||||
spdlog::info("Waiting for connection...");
|
||||
listenSock_.accept(clientSock_, clientAddr_);
|
||||
bool enable = true;
|
||||
clientSock_.setsockopt(SOL_SOCKET, SO_KEEPALIVE, (const char*)&enable, sizeof(enable));
|
||||
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA;
|
||||
::memset(&ptr->overlapped, 0, sizeof(OVERLAPPED));
|
||||
ptr->socket = std::make_shared<TCPSocket>(std::move(clientSock_));
|
||||
ptr->recvbytes = ptr->sendbytes = 0;
|
||||
ptr->wsabuf.buf = ptr->buf;
|
||||
ptr->wsabuf.len = 1500;
|
||||
ptr->IOCPInstance = &iocp_;
|
||||
|
||||
iocp_.registerSocket(ptr);
|
||||
|
||||
int returnData = iocp_.recv(ptr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,194 +1,9 @@
|
||||
#include "Socket/TCPSocket.hpp"
|
||||
#include "Socket/Log.hpp"
|
||||
#include "Socket/WSAManager.hpp"
|
||||
#include "Utils/ConfigManager.hpp"
|
||||
#include "Utils/Thread.hpp"
|
||||
#include "Utils/ThreadPool.hpp"
|
||||
#include "Utils/StringTokenizer.hpp"
|
||||
#include "Utils/Snowflake.hpp"
|
||||
#include "Socket/IOCP.hpp"
|
||||
#include "Packet/Packet.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#include "ServerManager/ServerManager.hpp"
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
void _IOCPClient(Chattr::ThreadPool* threadPool, Chattr::IOCPPASSINDATA* data);
|
||||
void _TCPSendClient(Chattr::ThreadPool* threadPool, Chattr::TCPSocket sock, std::queue<Chattr::ResponsePacket> packets);
|
||||
|
||||
int main() {
|
||||
auto config = Chattr::ConfigManager::load();
|
||||
Chattr::log::setDefaultLogger(config.logLevel, config.logFileName, config.logfileSize, config.logfileCount);
|
||||
Chattr::ThreadPool threadPool(0);
|
||||
Chattr::IOCP iocp;
|
||||
iocp.init(&threadPool, _IOCPClient);
|
||||
|
||||
Chattr::TCPSocket sock;
|
||||
struct Chattr::Address serveraddr;
|
||||
Chattr::TCPSocket clientSock;
|
||||
struct Chattr::Address clientAddr;
|
||||
if (config.ipVersion == 4) {
|
||||
sock.init(AF_INET);
|
||||
serveraddr.set(AF_INET, INADDR_ANY, config.listenPort);
|
||||
clientAddr.setType(AF_INET);
|
||||
}
|
||||
else if (config.ipVersion == 6) {
|
||||
sock.init(AF_INET6);
|
||||
serveraddr.set(AF_INET6, in6addr_any, config.listenPort);
|
||||
clientAddr.setType(AF_INET6);
|
||||
}
|
||||
sock.bind(serveraddr);
|
||||
sock.listen(SOMAXCONN);
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD pid = GetCurrentProcessId();
|
||||
#elif __linux__
|
||||
pid_t pid = getpid();
|
||||
#endif
|
||||
spdlog::debug("PID : {}", pid);
|
||||
|
||||
while (true) {
|
||||
spdlog::info("Waiting for connection...");
|
||||
sock.accept(clientSock, clientAddr);
|
||||
bool enable = true;
|
||||
clientSock.setsockopt(SOL_SOCKET, SO_KEEPALIVE, (const char *)&enable, sizeof(enable));
|
||||
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA;
|
||||
::memset(&ptr->overlapped, 0, sizeof(OVERLAPPED));
|
||||
ptr->socket = std::move(clientSock);
|
||||
ptr->recvbytes = ptr->sendbytes = 0;
|
||||
ptr->wsabuf.buf = ptr->buf;
|
||||
ptr->wsabuf.len = 1500;
|
||||
ptr->IOCPInstance = &iocp;
|
||||
|
||||
iocp.registerSocket(ptr);
|
||||
|
||||
int returnData = iocp.recv(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void _IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDATA* data) {
|
||||
Chattr::Packet pack;
|
||||
int packetSize = 1500;
|
||||
int totalRedSize = 0;
|
||||
int redSize = 0;
|
||||
|
||||
if (data->wsabuf.len == data->sendbytes)
|
||||
return;
|
||||
|
||||
memcpy(pack.serialized, data->wsabuf.buf, data->wsabuf.len);
|
||||
|
||||
std::string recvString;
|
||||
bool packetError = false;
|
||||
Chattr::DataPostPacket dataPostPacket;
|
||||
|
||||
Chattr::ResponsePacket packet;
|
||||
std::queue<Chattr::ResponsePacket> responsePackets;
|
||||
|
||||
switch (pack.__data.packetType) {
|
||||
case Chattr::PacketType::PACKET_POST:
|
||||
if (pack.__data.requestType != Chattr::RequestType::DATA){
|
||||
packetError = true;
|
||||
break;
|
||||
}
|
||||
switch (pack.__data.dataType) {
|
||||
case Chattr::DataType::TEXT:
|
||||
std::memcpy(&dataPostPacket.serialized, &pack, 1500);
|
||||
dataPostPacket.convToH();
|
||||
dataPostPacket.__data.packetLength = (dataPostPacket.__data.packetLength > 1487) ? 1487 : dataPostPacket.__data.packetLength;
|
||||
recvString = std::string((char *)dataPostPacket.__data.data, dataPostPacket.__data.packetLength - (sizeof(std::uint16_t)*4));
|
||||
spdlog::info("Red size : {}, {} from : [{}]", data->wsabuf.len, recvString, (std::string)data->socket.remoteAddr);
|
||||
break;
|
||||
case Chattr::DataType::BINARY:
|
||||
break;
|
||||
default:
|
||||
packet.__data.packetType = Chattr::PacketType::PACKET_RESPONSE;
|
||||
packet.__data.requestType = Chattr::RequestType::DATA;
|
||||
packet.__data.dataType = Chattr::DataType::TEXT;
|
||||
packet.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
packet.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
packet.convToN();
|
||||
responsePackets.push(packet);
|
||||
packetError = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Chattr::PacketType::PACKET_REQUEST:
|
||||
switch (pack.__data.requestType) {
|
||||
case Chattr::RequestType::LOGIN:
|
||||
break;
|
||||
case Chattr::RequestType::ROOM_CREATE:
|
||||
break;
|
||||
case Chattr::RequestType::ROOM_LIST:
|
||||
break;
|
||||
case Chattr::RequestType::ROOM_JOIN:
|
||||
break;
|
||||
case Chattr::RequestType::ROOM_EXIT:
|
||||
break;
|
||||
case Chattr::RequestType::USERS_LIST:
|
||||
break;
|
||||
default:
|
||||
packet.__data.packetType = Chattr::PacketType::PACKET_RESPONSE;
|
||||
packet.__data.requestType = Chattr::RequestType::DATA;
|
||||
packet.__data.dataType = Chattr::DataType::TEXT;
|
||||
packet.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
packet.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
packet.convToN();
|
||||
responsePackets.push(packet);
|
||||
packetError = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Chattr::PacketType::PACKET_CONTINUE:
|
||||
break;
|
||||
default:
|
||||
packet.__data.packetType = Chattr::PacketType::PACKET_RESPONSE;
|
||||
packet.__data.requestType = Chattr::RequestType::DATA;
|
||||
packet.__data.dataType = Chattr::DataType::TEXT;
|
||||
packet.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
packet.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
packet.convToN();
|
||||
responsePackets.push(packet);
|
||||
packetError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
packet.__data.packetType = Chattr::PacketType::PACKET_RESPONSE;
|
||||
packet.__data.requestType = Chattr::RequestType::DATA;
|
||||
packet.__data.dataType = Chattr::DataType::TEXT;
|
||||
packet.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||
packet.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||
packet.convToN();
|
||||
|
||||
memcpy(data->wsabuf.buf, packet.serialized, data->wsabuf.len);
|
||||
|
||||
int returnData = data->IOCPInstance->send(data, 0);
|
||||
|
||||
// int returnData = data->IOCPInstance->recv(data);
|
||||
}
|
||||
|
||||
void _TCPSendClient(Chattr::ThreadPool* thread, Chattr::TCPSocket sock, std::queue<Chattr::ResponsePacket> packets) {
|
||||
Chattr::ResponsePacket pack = packets.front();
|
||||
packets.pop();
|
||||
int packetSize = 1500;
|
||||
int totalSentSize = 0;
|
||||
int sentSize = 0;
|
||||
|
||||
spdlog::info("Sending to: [{}]", (std::string)sock.remoteAddr);
|
||||
|
||||
while (totalSentSize < packetSize) {
|
||||
|
||||
sentSize = sock.send(&pack.serialized, 1500 - totalSentSize, 0);
|
||||
|
||||
if (sentSize <= 0) {
|
||||
spdlog::info("Client disconnected. [{}]", (std::string)sock.remoteAddr);
|
||||
return;
|
||||
}
|
||||
totalSentSize += sentSize;
|
||||
}
|
||||
|
||||
/*if (packets.empty())
|
||||
thread->enqueueJob(_TCPRecvClient, std::move(sock));
|
||||
else
|
||||
thread->enqueueJob(_TCPSendClient, std::move(sock), packets);*/
|
||||
Chattr::ServerManager server;
|
||||
server.init();
|
||||
server.run();
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace Chattr {
|
||||
|
||||
void IOCP::registerSocket(Chattr::IOCPPASSINDATA* data) {
|
||||
#ifdef _WIN32
|
||||
HANDLE returnData = ::CreateIoCompletionPort((HANDLE)data->socket.sock, completionPort_, data->socket.sock, 0);
|
||||
HANDLE returnData = ::CreateIoCompletionPort((HANDLE)data->socket->sock, completionPort_, data->socket->sock, 0);
|
||||
if (returnData == 0)
|
||||
completionPort_ = returnData;
|
||||
#elif __linux__
|
||||
@@ -24,10 +24,10 @@ void IOCP::registerSocket(Chattr::IOCPPASSINDATA* data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int IOCP::recv(Chattr::IOCPPASSINDATA* data) {
|
||||
int IOCP::recv(Chattr::IOCPPASSINDATA* data, int bufferCount) {
|
||||
#ifdef _WIN32
|
||||
DWORD recvbytes = 0, flags = 0;
|
||||
return ::WSARecv(data->socket.sock, &data->wsabuf, 1, &recvbytes, &flags, &data->overlapped, NULL);
|
||||
return ::WSARecv(data->socket->sock, &data->wsabuf, bufferCount, &recvbytes, &flags, &data->overlapped, NULL);
|
||||
#elif __linux__
|
||||
struct epoll_event ev;
|
||||
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||
@@ -36,10 +36,10 @@ int IOCP::recv(Chattr::IOCPPASSINDATA* data) {
|
||||
#endif
|
||||
}
|
||||
|
||||
int IOCP::send(Chattr::IOCPPASSINDATA* data, int __flags) {
|
||||
int IOCP::send(Chattr::IOCPPASSINDATA* data, int bufferCount, int __flags) {
|
||||
#ifdef _WIN32
|
||||
DWORD sendbytes = 0;
|
||||
return ::WSASend(data->socket.sock, &data->wsabuf, 1, &sendbytes, __flags, &data->overlapped, NULL);
|
||||
return ::WSASend(data->socket->sock, &data->wsabuf, bufferCount, &sendbytes, __flags, &data->overlapped, NULL);
|
||||
#elif __linux__
|
||||
struct epoll_event ev;
|
||||
ev.events = EPOLLOUT | EPOLLONESHOT;
|
||||
|
||||
@@ -80,12 +80,24 @@ int Socket::sendto(const void *__buf, size_t __n, int __flags, struct Address __
|
||||
return retVal;
|
||||
}
|
||||
|
||||
Socket::Socket(const Socket& other_) {
|
||||
memcpy(this, &other_, sizeof(Socket));
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
Socket::Socket(Socket &&other_) noexcept {
|
||||
other_.valid_ = false;
|
||||
memcpy(this, &other_, sizeof(Socket));
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
Socket& Socket::operator=(const Socket& other_) {
|
||||
memcpy(this, &other_, sizeof(Socket));
|
||||
valid_ = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Socket& Socket::operator=(Socket && other_) noexcept {
|
||||
other_.valid_ = false;
|
||||
memcpy(this, &other_, sizeof(Socket));
|
||||
|
||||
@@ -5,7 +5,27 @@
|
||||
|
||||
namespace Chattr {
|
||||
|
||||
enum class PacketType : std::uint8_t {
|
||||
enum class PacketSet {
|
||||
LOGINREQUEST,
|
||||
ROOMCREATEREQUEST,
|
||||
ROOMLISTREQUEST,
|
||||
ROOMJOINREQUEST,
|
||||
ROOMEXITREQUEST,
|
||||
USERSLISTREQUEST,
|
||||
DATAPOSTTEXT,
|
||||
DATAPOSTBINARY,
|
||||
CONTINUE,
|
||||
RESPONSE,
|
||||
LOGINRESPONSE,
|
||||
ROOMCREATERESPONSE,
|
||||
ROOMLISTRESPONSE,
|
||||
ROOMJOINRESPONSE,
|
||||
ROOMEXITRESPONSE,
|
||||
USERSLISTRESPONSE,
|
||||
INVALID
|
||||
};
|
||||
|
||||
enum class PacketCategory : std::uint8_t {
|
||||
PACKET_POST,
|
||||
PACKET_REQUEST,
|
||||
PACKET_RESPONSE,
|
||||
@@ -22,6 +42,8 @@ enum class RequestType : std::uint8_t {
|
||||
DATA
|
||||
};
|
||||
|
||||
#define MOREFRAG 0b00000001
|
||||
|
||||
enum class DataType : std::uint8_t {
|
||||
TEXT,
|
||||
BINARY
|
||||
@@ -31,7 +53,7 @@ class alignas(4) Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
@@ -49,106 +71,48 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(4) LoginRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
};
|
||||
class alignas(4) LoginRequestPacket : public Packet {};
|
||||
|
||||
class alignas(4) RoomCreateRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
};
|
||||
class alignas(4) RoomCreateRequestPacket : public Packet {};
|
||||
|
||||
class alignas(4) RoomListRequestPacket : public Packet {
|
||||
class alignas(4) RoomListRequestPacket : public Packet {};
|
||||
|
||||
class alignas(4) RoomJoinRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint32_t roomCount;
|
||||
std::uint16_t destId[4];
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
std::uint8_t* convToN() {
|
||||
__data.packetLength = ::htons(__data.packetLength);
|
||||
__data.roomCount = ::htonl(__data.roomCount);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.destId[i] = ::htons(__data.destId[i]);
|
||||
return serialized;
|
||||
}
|
||||
std::uint8_t* convToH() {
|
||||
__data.packetLength = ::ntohs(__data.packetLength);
|
||||
__data.roomCount = ::ntohl(__data.roomCount);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.destId[i] = ::ntohs(__data.destId[i]);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(4) RoomJoinRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
};
|
||||
class alignas(4) RoomExitRequestPacket : public Packet {};
|
||||
|
||||
class alignas(4) RoomExitRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint8_t data[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
};
|
||||
|
||||
class alignas(4) UsersListRequestPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
};
|
||||
class alignas(4) UsersListRequestPacket : public Packet {};
|
||||
|
||||
class alignas(4) DataPostPacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
@@ -175,13 +139,27 @@ class alignas(4) ContinuePacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
std::uint8_t padding[2];
|
||||
PacketCategory packetType;
|
||||
std::uint8_t padding;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
std::uint16_t destId[4];
|
||||
std::uint8_t data[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
std::uint8_t* convToN() {
|
||||
__data.packetLength = ::htons(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.destId[i] = ::htons(__data.destId[i]);
|
||||
return serialized;
|
||||
}
|
||||
std::uint8_t* convToH() {
|
||||
__data.packetLength = ::ntohs(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.destId[i] = ::ntohs(__data.destId[i]);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
|
||||
enum class ResponseStatusCode : std::uint16_t {
|
||||
@@ -201,7 +179,7 @@ class alignas(4) ResponsePacket : public Packet {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
@@ -222,29 +200,98 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
//enum class RequestType : std::uint8_t {
|
||||
// LOGIN,
|
||||
// ROOM_CREATE,
|
||||
// ROOM_LIST,
|
||||
// ROOM_JOIN,
|
||||
// ROOM_EXIT,
|
||||
// USERS_LIST,
|
||||
// DATA
|
||||
//};
|
||||
class alignas(4) LoginResponsePacket : public ResponsePacket {};
|
||||
|
||||
class alignas(4) LoginResponsePacket : public ResponsePacket {
|
||||
class alignas(4) RoomCreateResponsePacket : public ResponsePacket {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketType packetType;
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
ResponseStatusCode responseStatusCode;
|
||||
std::uint16_t createdRoomId[4];
|
||||
std::uint8_t data[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
std::uint8_t* convToN() {
|
||||
__data.packetLength = ::htons(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.createdRoomId[i] = ::htons(__data.createdRoomId[i]);
|
||||
return serialized;
|
||||
}
|
||||
std::uint8_t* convToH() {
|
||||
__data.packetLength = ::ntohs(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.createdRoomId[i] = ::ntohs(__data.createdRoomId[i]);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(4) RoomListResponsePacket : public ResponsePacket {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
ResponseStatusCode responseStatusCode;
|
||||
std::uint32_t roomCount;
|
||||
std::uint16_t roomId[4];
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
std::uint8_t* convToN() {
|
||||
__data.packetLength = ::htons(__data.packetLength);
|
||||
__data.roomCount = ::htonl(__data.roomCount);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.roomId[i] = ::htons(__data.roomId[i]);
|
||||
return serialized;
|
||||
}
|
||||
std::uint8_t* convToH() {
|
||||
__data.packetLength = ::ntohs(__data.packetLength);
|
||||
__data.roomCount = ::ntohl(__data.roomCount);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.roomId[i] = ::ntohs(__data.roomId[i]);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
|
||||
class alignas(4) RoomJoinResponsePacket : public ResponsePacket {};
|
||||
|
||||
class alignas(4) RoomExitResponsePacket : public ResponsePacket {};
|
||||
|
||||
class alignas(4) UsersListResponsePacket : public ResponsePacket {
|
||||
public:
|
||||
union {
|
||||
struct {
|
||||
PacketCategory packetType;
|
||||
RequestType requestType;
|
||||
DataType dataType;
|
||||
std::uint16_t packetLength;
|
||||
ResponseStatusCode responseStatusCode;
|
||||
std::uint32_t usersCount;
|
||||
std::uint16_t userId[4];
|
||||
std::uint8_t name[];
|
||||
} __data;
|
||||
std::uint8_t serialized[1500] = "";
|
||||
};
|
||||
std::uint8_t* convToN() {
|
||||
__data.packetLength = ::htons(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.userId[i] = ::htons(__data.userId[i]);
|
||||
return serialized;
|
||||
}
|
||||
std::uint8_t* convToH() {
|
||||
__data.packetLength = ::ntohs(__data.packetLength);
|
||||
for (int i = 0; i < 4; i++)
|
||||
__data.userId[i] = ::ntohs(__data.userId[i]);
|
||||
return serialized;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "Socket/TCPSocket.hpp"
|
||||
#include "Socket/Log.hpp"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -25,10 +27,11 @@ class IOCP;
|
||||
|
||||
struct IOCPPASSINDATA {
|
||||
OVERLAPPED overlapped;
|
||||
TCPSocket socket;
|
||||
std::shared_ptr<TCPSocket> socket;
|
||||
char buf[1501];
|
||||
int recvbytes;
|
||||
int sendbytes;
|
||||
std::uint32_t recvbytes;
|
||||
std::uint32_t sendbytes;
|
||||
std::uint32_t transfrredbytes;
|
||||
WSABUF wsabuf;
|
||||
IOCP* IOCPInstance;
|
||||
};
|
||||
@@ -38,17 +41,18 @@ public:
|
||||
#ifdef _WIN32
|
||||
static void iocpWather(ThreadPool* threadPool, HANDLE completionPort_, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
|
||||
DWORD tid = GetCurrentThreadId();
|
||||
spdlog::debug("Waiting IO to complete on TID: {}.", tid);
|
||||
spdlog::trace("Waiting IO to complete on TID: {}.", tid);
|
||||
IOCPPASSINDATA* data;
|
||||
SOCKET sock;
|
||||
DWORD cbTransfrred;
|
||||
int retVal = GetQueuedCompletionStatus(completionPort_, &cbTransfrred, (PULONG_PTR)&sock, (LPOVERLAPPED*)&data, INFINITE);
|
||||
if (retVal == 0 || cbTransfrred == 0) {
|
||||
spdlog::info("Client disconnected. [{}]", (std::string)(data->socket.remoteAddr));
|
||||
spdlog::debug("Client disconnected. [{}]", (std::string)(data->socket->remoteAddr));
|
||||
delete data;
|
||||
threadPool->enqueueJob(iocpWather, completionPort_, callback);
|
||||
return;
|
||||
}
|
||||
data->transfrredbytes = cbTransfrred;
|
||||
threadPool->enqueueJob(callback, data);
|
||||
threadPool->enqueueJob(iocpWather, completionPort_, callback);
|
||||
};
|
||||
@@ -63,7 +67,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
spdlog::info("reading on tid: {} [{}]", tid, (std::string)iocpData->socket.remoteAddr);
|
||||
spdlog::trace("reading on tid: {} [{}]", tid, (std::string)iocpData->socket.remoteAddr);
|
||||
|
||||
int redSize = 0;
|
||||
int packetSize = iocpData->wsabuf.len;
|
||||
@@ -73,7 +77,7 @@ public:
|
||||
redSize = iocpData->socket.recv(iocpData->buf, packetSize - totalRedSize, 0);
|
||||
|
||||
if (redSize <= 0) {
|
||||
spdlog::info("Client disconnected. [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
spdlog::debug("Client disconnected. [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
::epoll_ctl(epollfd, EPOLL_CTL_DEL, iocpData->socket.sock, NULL);
|
||||
delete iocpData;
|
||||
return;
|
||||
@@ -91,13 +95,13 @@ public:
|
||||
int totalSentSize = 0;
|
||||
int sentSize = 0;
|
||||
|
||||
spdlog::info("Sending to: [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
spdlog::trace("Sending to: [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
|
||||
while (totalSentSize < packetSize) {
|
||||
sentSize = iocpData->socket.send(iocpData->buf, packetSize - totalSentSize, 0);
|
||||
|
||||
if (sentSize <= 0) {
|
||||
spdlog::info("Client disconnected. [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
spdlog::debug("Client disconnected. [{}]", (std::string)iocpData->socket.remoteAddr);
|
||||
::epoll_ctl(epollfd, EPOLL_CTL_DEL, iocpData->socket.sock, NULL);
|
||||
delete iocpData;
|
||||
return;
|
||||
@@ -112,7 +116,7 @@ public:
|
||||
struct epoll_event events[FD_SETSIZE];
|
||||
pthread_t tid = pthread_self();
|
||||
|
||||
spdlog::debug("epoll waiting on {}", tid);
|
||||
spdlog::trace("epoll waiting on {}", tid);
|
||||
int nready = ::epoll_wait(epollfd, events, FD_SETSIZE, -1);
|
||||
|
||||
for (int i=0; i<nready; i++) {
|
||||
@@ -164,8 +168,8 @@ public:
|
||||
|
||||
void registerSocket(Chattr::IOCPPASSINDATA* data);
|
||||
|
||||
int recv(Chattr::IOCPPASSINDATA* data);
|
||||
int send(Chattr::IOCPPASSINDATA* data, int __flags);
|
||||
int recv(Chattr::IOCPPASSINDATA* data, int bufferCount);
|
||||
int send(Chattr::IOCPPASSINDATA* data, int bufferCount, int __flags);
|
||||
|
||||
private:
|
||||
struct Chattr::WSAManager wsaManager;
|
||||
|
||||
@@ -23,9 +23,9 @@ public:
|
||||
int recvfrom(void *__restrict __buf, size_t __n, int __flags, struct Address& __addr);
|
||||
int sendto(const void *__buf, size_t __n, int __flags, struct Address __addr);
|
||||
|
||||
Socket(const Socket&) = delete;
|
||||
Socket(const Socket&);
|
||||
Socket(Socket&&) noexcept;
|
||||
Socket& operator=(const Socket&) = delete;
|
||||
Socket& operator=(const Socket&);
|
||||
Socket& operator=(Socket&&) noexcept;
|
||||
|
||||
struct Address bindAddr = {};
|
||||
|
||||
@@ -12,6 +12,19 @@ struct Snowflake {
|
||||
};
|
||||
std::uint64_t snowflake;
|
||||
};
|
||||
|
||||
bool operator==(const Snowflake& other) const {
|
||||
return snowflake == other.snowflake;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<Chattr::Snowflake> {
|
||||
std::size_t operator()(const Chattr::Snowflake& k) const {
|
||||
return std::hash<uint64_t>{}(k.snowflake);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -64,9 +64,9 @@ public:
|
||||
handle_ = (HANDLE)_beginthreadex(nullptr, 0, thread_func, funcPtr, 0, nullptr);
|
||||
#elif __linux__
|
||||
int rc = pthread_create(&handle_, NULL, thread_func, funcPtr);
|
||||
#endif
|
||||
if (handle_ <= 0 || rc != 0)
|
||||
log::critical("pthread_create()");
|
||||
#endif
|
||||
}
|
||||
~Thread();
|
||||
|
||||
|
||||
@@ -24,13 +24,4 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <gsl/gsl>
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace Chattr {
|
||||
static struct _EPOQUE {
|
||||
_EPOQUE() {
|
||||
EPOQUE = std::chrono::system_clock::now();
|
||||
}
|
||||
std::chrono::system_clock::time_point EPOQUE;
|
||||
} __EPOQUE__;
|
||||
}
|
||||
#include "spdlog/spdlog.h"
|
||||
Reference in New Issue
Block a user