Compare commits
59 Commits
c660c41657
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b486008fa | |||
| be6417cd40 | |||
| 7c382bead0 | |||
| 91a3cfd9f4 | |||
| 89306442ae | |||
| d5342b5f61 | |||
| 25527405d5 | |||
| 65b7aa8f6e | |||
| 6c1b0c6852 | |||
| 7157e67040 | |||
| be6ea6e92f | |||
| 20f8c7b29d | |||
| 74e73ccc23 | |||
| 0aa312f67e | |||
| 78f2bfe2f6 | |||
| 1ae7486cd5 | |||
| df35dd43c1 | |||
| 914cfcba32 | |||
| 87cc1c7119 | |||
| 46abb2b2b9 | |||
| 993d41736d | |||
| 8ffe3a7519 | |||
| e0a371d536 | |||
| 63428ebf4d | |||
| 016d923d64 | |||
| 0cd43f3d42 | |||
| b5b7329d32 | |||
| 4e03b84b1d | |||
| 6ad787eed7 | |||
| 4bbb6837e8 | |||
| 06e97023d5 | |||
| f765c163bf | |||
| 181d30ff28 | |||
| 7fabc98456 | |||
| 925d3874c8 | |||
| 40d522e755 | |||
| 14c325c428 | |||
| 6622cc8939 | |||
| 4fb5bdd4cb | |||
| d3161edb36 | |||
| 9451b4da40 | |||
| af414e58a1 | |||
| b662823726 | |||
| 36725fd8f2 | |||
| 748e69cc2f | |||
| cf8166f41c | |||
|
|
cebd6bb9e9 | ||
| 89633f94a2 | |||
| 1703974310 | |||
|
|
1c6404530d | ||
| 4cb1d22fe6 | |||
| 41c75b9dca | |||
| f76e1b9a5d | |||
| 8aa0a4c4e6 | |||
| a02fb5da88 | |||
| df95bde226 | |||
| 1418cf6880 | |||
| 81d912cc5e | |||
| d13a9ca474 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
build/*
|
build/*
|
||||||
out
|
out
|
||||||
.vs
|
.vs
|
||||||
|
*.json
|
||||||
|
*.log
|
||||||
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@@ -71,6 +71,21 @@
|
|||||||
"queue": "cpp",
|
"queue": "cpp",
|
||||||
"stack": "cpp",
|
"stack": "cpp",
|
||||||
"fstream": "cpp",
|
"fstream": "cpp",
|
||||||
"regex": "cpp"
|
"regex": "cpp",
|
||||||
|
"assert": "cpp",
|
||||||
|
"pointers": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"zstring": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"util": "cpp",
|
||||||
|
"expected": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"__config": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"valarray": "cpp",
|
||||||
|
"__tree": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"any": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
set(PROJECT_NAME "Chattring")
|
set(PROJECT_NAME "Chattring")
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ FetchContent_MakeAvailable(JSONCPP)
|
|||||||
|
|
||||||
file(GLOB_RECURSE additional_sources CONFIGURE_DEPENDS
|
file(GLOB_RECURSE additional_sources CONFIGURE_DEPENDS
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
@@ -46,6 +47,14 @@ target_link_libraries(${PROJECT_NAME} PRIVATE jsoncpp_lib)
|
|||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32)
|
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32)
|
||||||
endif()
|
endif()
|
||||||
|
if(UNIX AND NOT APPLE AND CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
INSTALL_RPATH "$ORIGIN"
|
||||||
|
SKIP_BUILD_RPATH FALSE
|
||||||
|
BUILD_RPATH "$ORIGIN"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
||||||
@@ -55,17 +64,27 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
|||||||
|
|
||||||
target_precompile_headers(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../include/precomp.hpp")
|
target_precompile_headers(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../include/precomp.hpp")
|
||||||
|
|
||||||
if(WIN32)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
"$<TARGET_FILE:spdlog>"
|
||||||
"$<TARGET_FILE:spdlog>"
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
COMMENT "Copying spdlog DLL/so to output directory"
|
||||||
COMMENT "Copying spdlog DLL to output directory"
|
)
|
||||||
)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
"$<TARGET_FILE:jsoncpp_lib>"
|
||||||
"$<TARGET_FILE:jsoncpp_lib>"
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
COMMENT "Copying jsoncpp DLL/so to output directory"
|
||||||
COMMENT "Copying jsoncpp DLL to output directory"
|
)
|
||||||
)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
endif()
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"$<TARGET_FILE_DIR:spdlog>"
|
||||||
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
|
COMMENT "Copying spdlog library files and symlinks to output directory"
|
||||||
|
)
|
||||||
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"$<TARGET_FILE_DIR:jsoncpp_lib>"
|
||||||
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
|
COMMENT "Copying jsoncpp library files and symlinks to output directory"
|
||||||
|
)
|
||||||
106
Client/include/ClientManager/ClientManager.hpp
Normal file
106
Client/include/ClientManager/ClientManager.hpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils/ThreadPool.hpp"
|
||||||
|
#include "Utils/ConfigManager.hpp"
|
||||||
|
#include "Socket/IOCP.hpp"
|
||||||
|
#include "Packet/Packet.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
class ClientManager {
|
||||||
|
public:
|
||||||
|
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) {
|
||||||
|
sock_.init(AF_INET);
|
||||||
|
serveraddr.set(AF_INET, config.IP, config.Port);
|
||||||
|
}
|
||||||
|
else if (config.ipVersion == 6) {
|
||||||
|
sock_.init(AF_INET6);
|
||||||
|
serveraddr.set(AF_INET6, config.IP, config.Port);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock_.connect(serveraddr) == INVALID_SOCKET) {
|
||||||
|
spdlog::error("{}", strerror(errno));
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_ = new IOCPPASSINDATA;
|
||||||
|
::memset(&data_->overlapped, 0, sizeof(OVERLAPPED));
|
||||||
|
data_->socket = std::make_shared<TCPSocket>(std::move(sock_));
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->wsabuf.buf = data_->buf;
|
||||||
|
data_->wsabuf.len = 1500;
|
||||||
|
data_->IOCPInstance = &iocp_;
|
||||||
|
iocp_.registerSocket(data_);
|
||||||
|
|
||||||
|
spdlog::info("Connection established from [{}]", (std::string)serveraddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
init([this](ThreadPool* thread, IOCPPASSINDATA* data) {
|
||||||
|
this->_IOCPClient(thread, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void processResponsePacket(ResponsePacket responsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processLoginResponsePacket(LoginResponsePacket loginResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomCreateResponsePacket(RoomCreateResponsePacket roomCreateResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomListResponse(RoomListResponsePacket roomListResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomJoinResponsePacket(RoomJoinResponsePacket roomJoinResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomExitResponsePacket(RoomExitResponsePacket roomExitResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processUsersListResponsePacket(UsersListResponsePacket usersListResponsePacket, IOCPPASSINDATA* data);
|
||||||
|
void processDataPostPacket(DataPostPacket dataPostPacket, IOCPPASSINDATA* data);
|
||||||
|
void processContinuePacket(ContinuePacket continuePacket, IOCPPASSINDATA* data);
|
||||||
|
|
||||||
|
void sendMessage(Snowflake ID, std::string message);
|
||||||
|
|
||||||
|
void registerUser(std::string userName);
|
||||||
|
void deleteUser(Snowflake UID);
|
||||||
|
void getUserList();
|
||||||
|
|
||||||
|
void createRoom(std::string roomName);
|
||||||
|
void getRoomList();
|
||||||
|
|
||||||
|
void joinRoom(Snowflake UID, Snowflake RID);
|
||||||
|
void exitRoom(Snowflake UID, Snowflake RID);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
private:
|
||||||
|
ThreadPool threadPool_;
|
||||||
|
IOCP iocp_;
|
||||||
|
|
||||||
|
Snowflake myID_;
|
||||||
|
Snowflake myRoomID_;
|
||||||
|
|
||||||
|
IOCPPASSINDATA* data_;
|
||||||
|
|
||||||
|
bool inRoom_ = false;
|
||||||
|
|
||||||
|
std::future<ResponsePacket> lastResponsePacket_;
|
||||||
|
|
||||||
|
TCPSocket sock_;
|
||||||
|
|
||||||
|
std::mutex resourceMutex_;
|
||||||
|
std::mutex screenMutex_;
|
||||||
|
|
||||||
|
std::queue<std::string> messageQueue_;
|
||||||
|
std::vector<std::string> messageHistory_;
|
||||||
|
|
||||||
|
std::unordered_map<Snowflake, std::string> roomNames_;
|
||||||
|
std::unordered_map<std::string, Snowflake> findRoomId_;
|
||||||
|
|
||||||
|
std::unordered_map<Snowflake, std::string> userNames_;
|
||||||
|
std::unordered_map<std::string, Snowflake> findUserId_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
490
Client/src/ClientManager/ClientManager.cpp
Normal file
490
Client/src/ClientManager/ClientManager.cpp
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
#include "ClientManager/ClientManager.hpp"
|
||||||
|
#include "Utils/ConfigManager.hpp"
|
||||||
|
#include "Utils/StringTokenizer.hpp"
|
||||||
|
#include "Socket/Log.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
void ClientManager::_IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
if (data->event == IOCPEVENT::QUIT) {
|
||||||
|
delete data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chattr::Packet pack;
|
||||||
|
int packetSize = data->transferredbytes;
|
||||||
|
|
||||||
|
if (data->event == IOCPEVENT::WRITE && data->transferredbytes >= data->wsabuf.len) {
|
||||||
|
data->event = IOCPEVENT::READ;
|
||||||
|
data->wsabuf.len = 1500;
|
||||||
|
data->IOCPInstance->recv(data, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pack.serialized, data->wsabuf.buf, data->wsabuf.len);
|
||||||
|
|
||||||
|
std::uint16_t packetLength = ::ntohs(pack.__data.packetLength);
|
||||||
|
|
||||||
|
// if (data->event == IOCPEVENT::READ && data->transferredbytes < packetLength + 8) {
|
||||||
|
// data->IOCPInstance->recv(data, 1);
|
||||||
|
// data->wsabuf.len = 1500;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
pack.convToH();
|
||||||
|
PacketSet packetSet = packetParser(pack);
|
||||||
|
pack.convToN();
|
||||||
|
switch (packetSet) {
|
||||||
|
case PacketSet::LOGINRESPONSE: {
|
||||||
|
LoginResponsePacket loginResponsePacket;
|
||||||
|
std::memcpy(&loginResponsePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
loginResponsePacket.convToH();
|
||||||
|
|
||||||
|
lastResponsePacket_ = std::future<ResponsePacket>();
|
||||||
|
auto promise = std::promise<ResponsePacket>();
|
||||||
|
lastResponsePacket_ = promise.get_future();
|
||||||
|
promise.set_value(loginResponsePacket);
|
||||||
|
|
||||||
|
processLoginResponsePacket(loginResponsePacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMCREATERESPONSE:
|
||||||
|
case PacketSet::ROOMLISTRESPONSE: {
|
||||||
|
RoomListResponsePacket roomListResponsePacket;
|
||||||
|
std::memcpy(&roomListResponsePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
roomListResponsePacket.convToH();
|
||||||
|
|
||||||
|
lastResponsePacket_ = std::future<ResponsePacket>();
|
||||||
|
auto promise = std::promise<ResponsePacket>();
|
||||||
|
lastResponsePacket_ = promise.get_future();
|
||||||
|
promise.set_value(roomListResponsePacket);
|
||||||
|
processRoomListResponse(roomListResponsePacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMJOINRESPONSE:
|
||||||
|
case PacketSet::ROOMEXITRESPONSE:
|
||||||
|
case PacketSet::USERSLISTRESPONSE: {
|
||||||
|
UsersListResponsePacket usersListResponsePacket;
|
||||||
|
std::memcpy(&usersListResponsePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
usersListResponsePacket.convToH();
|
||||||
|
|
||||||
|
lastResponsePacket_ = std::future<ResponsePacket>();
|
||||||
|
auto promise = std::promise<ResponsePacket>();
|
||||||
|
lastResponsePacket_ = promise.get_future();
|
||||||
|
promise.set_value(usersListResponsePacket);
|
||||||
|
processUsersListResponsePacket(usersListResponsePacket, data);
|
||||||
|
}
|
||||||
|
case PacketSet::RESPONSE: {
|
||||||
|
ResponsePacket responsePacket;
|
||||||
|
std::memcpy(&responsePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
responsePacket.convToH();
|
||||||
|
|
||||||
|
lastResponsePacket_ = std::future<ResponsePacket>();
|
||||||
|
auto promise = std::promise<ResponsePacket>();
|
||||||
|
lastResponsePacket_ = promise.get_future();
|
||||||
|
promise.set_value(responsePacket);
|
||||||
|
|
||||||
|
processResponsePacket(responsePacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::DATAPOST: {
|
||||||
|
DataPostPacket dataPostPacket;
|
||||||
|
std::memcpy(&dataPostPacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
dataPostPacket.convToH();
|
||||||
|
processDataPostPacket(dataPostPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::CONTINUE: {
|
||||||
|
ContinuePacket continuePacket;
|
||||||
|
std::memcpy(&continuePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
continuePacket.convToH();
|
||||||
|
processContinuePacket(continuePacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::INVALID:
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iocp_.recv(data_, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketSet ClientManager::packetParser(Packet Packet) {
|
||||||
|
if (Packet.__data.packetLength < 0 || Packet.__data.packetLength > 1492)
|
||||||
|
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:
|
||||||
|
case DataType::BINARY:
|
||||||
|
return PacketSet::DATAPOST;
|
||||||
|
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::ROOM_USERS_LIST:
|
||||||
|
return PacketSet::ROOMUSERSLISTREQUEST;
|
||||||
|
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::ROOM_USERS_LIST:
|
||||||
|
return PacketSet::ROOMUSERSLISTRESPONSE;
|
||||||
|
case RequestType::DATA:
|
||||||
|
return PacketSet::RESPONSE;
|
||||||
|
default:
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketCategory::PACKET_CONTINUE:
|
||||||
|
return PacketSet::CONTINUE;
|
||||||
|
default:
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processResponsePacket(ResponsePacket responsePacket, IOCPPASSINDATA* data) {
|
||||||
|
responsePacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ClientManager::processLoginResponsePacket(LoginResponsePacket loginResponsePacket, IOCPPASSINDATA* data) {
|
||||||
|
::memcpy(&myID_.snowflake, loginResponsePacket.__data.yourId, sizeof(Snowflake));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processRoomCreateResponsePacket(RoomCreateResponsePacket roomCreateResponsePacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processRoomListResponse(RoomListResponsePacket roomListResponsePacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
Snowflake roomId;
|
||||||
|
::memcpy(&roomId.snowflake, roomListResponsePacket.__data.roomId, sizeof(Snowflake));
|
||||||
|
std::string roomName((char*)roomListResponsePacket.__data.name, roomListResponsePacket.__data.packetLength - (sizeof(std::uint16_t) * 7));
|
||||||
|
roomNames_[roomId] = roomName;
|
||||||
|
findRoomId_[roomName] = roomId;
|
||||||
|
|
||||||
|
spdlog::info("{}", roomName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processRoomJoinResponsePacket(RoomJoinResponsePacket roomJoinResponsePacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processRoomExitResponsePacket(RoomExitResponsePacket roomExitResponsePacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processUsersListResponsePacket(UsersListResponsePacket usersListResponsePacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
Snowflake userId;
|
||||||
|
::memcpy(&userId.snowflake, usersListResponsePacket.__data.userId, sizeof(Snowflake));
|
||||||
|
std::string userName((char*)usersListResponsePacket.__data.name, usersListResponsePacket.__data.packetLength - (sizeof(std::uint16_t) * 7));
|
||||||
|
userNames_[userId] = userName;
|
||||||
|
findUserId_[userName] = userId;
|
||||||
|
|
||||||
|
spdlog::info("{}", userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processDataPostPacket(DataPostPacket dataPostPacket, IOCPPASSINDATA* data) {
|
||||||
|
Snowflake sentUserId;
|
||||||
|
::memcpy(&sentUserId.snowflake, dataPostPacket.__data.sourceId, sizeof(Snowflake));
|
||||||
|
std::string sentUserName = userNames_[sentUserId];
|
||||||
|
std::string message((char*)dataPostPacket.__data.data, dataPostPacket.__data.packetLength - (sizeof(std::uint16_t) * 5));
|
||||||
|
|
||||||
|
spdlog::info("[{}] {}", sentUserName, message); // todo: pass data to main thread
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::processContinuePacket(ContinuePacket continuePacket, IOCPPASSINDATA* data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::sendMessage(Snowflake ID, std::string message) {
|
||||||
|
DataPostPacket dataPostPacket;
|
||||||
|
dataPostPacket.__data.packetType = PacketCategory::PACKET_POST;
|
||||||
|
dataPostPacket.__data.requestType = RequestType::DATA;
|
||||||
|
dataPostPacket.__data.dataType = DataType::TEXT;
|
||||||
|
dataPostPacket.__data.packetLength = message.size() + sizeof(Snowflake) * 2;
|
||||||
|
memcpy(dataPostPacket.__data.sourceId, &myID_.snowflake, sizeof(Snowflake));
|
||||||
|
memcpy(dataPostPacket.__data.destId, &ID.snowflake, sizeof(Snowflake));
|
||||||
|
memcpy(dataPostPacket.__data.data, message.c_str(), message.size());
|
||||||
|
|
||||||
|
int packetLength = dataPostPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = data_->sendbytes = packetLength + 8;
|
||||||
|
|
||||||
|
dataPostPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, dataPostPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::registerUser(std::string userName) {
|
||||||
|
LoginRequestPacket loginRequestPacket;
|
||||||
|
loginRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
loginRequestPacket.__data.requestType = RequestType::LOGIN;
|
||||||
|
loginRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
loginRequestPacket.__data.packetLength = userName.size();
|
||||||
|
memcpy(loginRequestPacket.__data.data, userName.c_str(), userName.size());
|
||||||
|
|
||||||
|
int packetLength = loginRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
loginRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, loginRequestPacket.serialized, packetLength + 8);
|
||||||
|
data_->sendbytes = packetLength + 8;
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::getUserList() {
|
||||||
|
if (inRoom_) {
|
||||||
|
RoomUsersListRequestPacket roomUsersListRequestPacket;
|
||||||
|
roomUsersListRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
roomUsersListRequestPacket.__data.requestType = RequestType::ROOM_USERS_LIST;
|
||||||
|
roomUsersListRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
roomUsersListRequestPacket.__data.packetLength = sizeof(Snowflake);
|
||||||
|
memcpy(roomUsersListRequestPacket.__data.roomId, &myRoomID_.snowflake, sizeof(Snowflake));
|
||||||
|
|
||||||
|
int packetLength = roomUsersListRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
roomUsersListRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, roomUsersListRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UsersListRequestPacket usersListRequestPacket;
|
||||||
|
usersListRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
usersListRequestPacket.__data.requestType = RequestType::USERS_LIST;
|
||||||
|
usersListRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
usersListRequestPacket.__data.packetLength = 0;
|
||||||
|
|
||||||
|
int packetLength = usersListRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
usersListRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, usersListRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::createRoom(std::string roomName) {
|
||||||
|
RoomCreateRequestPacket roomCreateRequestPacket;
|
||||||
|
roomCreateRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
roomCreateRequestPacket.__data.requestType = RequestType::ROOM_CREATE;
|
||||||
|
roomCreateRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
roomCreateRequestPacket.__data.packetLength = roomName.size();
|
||||||
|
memcpy(roomCreateRequestPacket.__data.data, roomName.c_str(), roomName.size());
|
||||||
|
|
||||||
|
int packetLength = roomCreateRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
roomCreateRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, roomCreateRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::getRoomList() {
|
||||||
|
RoomListRequestPacket roomListRequestPacket;
|
||||||
|
roomListRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
roomListRequestPacket.__data.requestType = RequestType::ROOM_LIST;
|
||||||
|
roomListRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
roomListRequestPacket.__data.packetLength = 0;
|
||||||
|
|
||||||
|
int packetLength = roomListRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
roomListRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, roomListRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::joinRoom(Snowflake UID, Snowflake RID) {
|
||||||
|
RoomJoinRequestPacket roomJoinRequestPacket;
|
||||||
|
roomJoinRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
roomJoinRequestPacket.__data.requestType = RequestType::ROOM_JOIN;
|
||||||
|
roomJoinRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
roomJoinRequestPacket.__data.packetLength = sizeof(Snowflake) * 2;
|
||||||
|
memcpy(roomJoinRequestPacket.__data.myId, &UID.snowflake, sizeof(Snowflake));
|
||||||
|
memcpy(roomJoinRequestPacket.__data.roomId, &RID.snowflake, sizeof(Snowflake));
|
||||||
|
|
||||||
|
int packetLength = roomJoinRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
roomJoinRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, roomJoinRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
inRoom_ = true;
|
||||||
|
myRoomID_ = RID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::exitRoom(Snowflake UID, Snowflake RID) {
|
||||||
|
RoomExitRequestPacket roomExitRequestPacket;
|
||||||
|
roomExitRequestPacket.__data.packetType = PacketCategory::PACKET_REQUEST;
|
||||||
|
roomExitRequestPacket.__data.requestType = RequestType::ROOM_EXIT;
|
||||||
|
roomExitRequestPacket.__data.dataType = DataType::BINARY;
|
||||||
|
roomExitRequestPacket.__data.packetLength = sizeof(Snowflake) * 2;
|
||||||
|
memcpy(roomExitRequestPacket.__data.myId, &UID.snowflake, sizeof(Snowflake));
|
||||||
|
memcpy(roomExitRequestPacket.__data.roomId, &RID.snowflake, sizeof(Snowflake));
|
||||||
|
|
||||||
|
int packetLength = roomExitRequestPacket.__data.packetLength;
|
||||||
|
|
||||||
|
data_->recvbytes = data_->sendbytes = 0;
|
||||||
|
data_->transferredbytes = 0;
|
||||||
|
data_->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
roomExitRequestPacket.convToN();
|
||||||
|
memcpy(data_->wsabuf.buf, roomExitRequestPacket.serialized, packetLength + 8);
|
||||||
|
iocp_.send(data_, 1, 0);
|
||||||
|
inRoom_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientManager::run() {
|
||||||
|
StringTokenizer tokenizer;
|
||||||
|
std::string nickname;
|
||||||
|
|
||||||
|
std::cout << "Please enter your nickname: ";
|
||||||
|
std::getline(std::cin, nickname);
|
||||||
|
tokenizer.set(nickname.c_str(), nickname.size());
|
||||||
|
|
||||||
|
registerUser(tokenizer.get()[0]);
|
||||||
|
|
||||||
|
std::cout << "Commads:" << std::endl;
|
||||||
|
std::cout << "/w <username> : Send direct message to specified user" << std::endl;
|
||||||
|
std::cout << "/join <roomname> : Enter specified room" << std::endl;
|
||||||
|
std::cout << "/exit : Exit current room" << std::endl;
|
||||||
|
std::cout << "/create <roomname> : Create room" << std::endl;
|
||||||
|
std::cout << "/userlist : Print all registred users" << std::endl;
|
||||||
|
std::cout << "/roomlist : Print all registred rooms" << std::endl;
|
||||||
|
std::cout << "/quit : Terminate this program" << std::endl;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::string input;
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock2(screenMutex_);
|
||||||
|
if (inRoom_) {
|
||||||
|
std::cout << "[" + nickname + "@" + roomNames_[myRoomID_] + "]" + " ";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "[" + nickname + "]" + " ";
|
||||||
|
}
|
||||||
|
lock2.unlock();
|
||||||
|
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
tokenizer.set(input.c_str(), input.size());
|
||||||
|
auto tokens = tokenizer.get();
|
||||||
|
if (tokens.size() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tokens[0] == "/w") {
|
||||||
|
if (findUserId_.find(tokens[1]) == findUserId_.end()) {
|
||||||
|
resourceMutex_.lock();
|
||||||
|
messageQueue_.push("User not found");
|
||||||
|
resourceMutex_.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Snowflake destId = findUserId_[tokens[1]];
|
||||||
|
|
||||||
|
std::string message;
|
||||||
|
for (int i = 2; i < tokens.size(); i++)
|
||||||
|
message += tokens[i] + " ";
|
||||||
|
|
||||||
|
sendMessage(destId, message);
|
||||||
|
}
|
||||||
|
else if (tokens[0] == "/join") {
|
||||||
|
std::string roomName;
|
||||||
|
for (int i = 1; i < tokens.size(); i++)
|
||||||
|
roomName += tokens[i];
|
||||||
|
|
||||||
|
if (findRoomId_.find(roomName) == findRoomId_.end()) {
|
||||||
|
resourceMutex_.lock();
|
||||||
|
messageQueue_.push("Room not found");
|
||||||
|
resourceMutex_.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
joinRoom(myID_, findRoomId_[roomName]);
|
||||||
|
}
|
||||||
|
else if (tokens[0] == "/exit") {
|
||||||
|
if (!inRoom_) {
|
||||||
|
resourceMutex_.lock();
|
||||||
|
messageQueue_.push("You are not in any room");
|
||||||
|
resourceMutex_.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
exitRoom(myID_, myRoomID_);
|
||||||
|
}
|
||||||
|
else if (tokens[0] == "/create") {
|
||||||
|
std::string roomName = tokens[1];
|
||||||
|
createRoom(roomName);
|
||||||
|
}
|
||||||
|
else if (tokens[0] == "/userlist")
|
||||||
|
getUserList();
|
||||||
|
else if (tokens[0] == "/roomlist")
|
||||||
|
getRoomList();
|
||||||
|
else if (tokens[0] == "/quit") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (inRoom_) {
|
||||||
|
std::string message;
|
||||||
|
for (int i = 0; i < tokens.size(); i++)
|
||||||
|
message += tokens[i];
|
||||||
|
sendMessage(myRoomID_, message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resourceMutex_.lock();
|
||||||
|
messageQueue_.push("Command not found");
|
||||||
|
resourceMutex_.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
Client/src/Socket/Log.cpp
Normal file
25
Client/src/Socket/Log.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "Socket/Log.hpp"
|
||||||
|
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "spdlog/sinks/msvc_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr::log {
|
||||||
|
|
||||||
|
void setDefaultLogger(spdlog::level::level_enum logLevel, gsl::czstring logFileName, std::uint32_t logFileSize, std::uint32_t logFileCount) {
|
||||||
|
std::vector<spdlog::sink_ptr> sinks;
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFileName, logFileSize, logFileCount, false));
|
||||||
|
#ifdef _WIN32
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
|
||||||
|
#endif
|
||||||
|
auto chatteringLogger = std::make_shared<spdlog::logger>("Chattering Logger", begin(sinks), end(sinks));
|
||||||
|
chatteringLogger->set_level(logLevel);
|
||||||
|
spdlog::set_default_logger(chatteringLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
56
Client/src/Utils/ConfigManager.cpp
Normal file
56
Client/src/Utils/ConfigManager.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include "Utils/ConfigManager.hpp"
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
ConfigManager::Config ConfigManager::load() {
|
||||||
|
Config config;
|
||||||
|
|
||||||
|
std::ifstream configfile("config.json", std::ifstream::binary);
|
||||||
|
if (!configfile.is_open()) {
|
||||||
|
std::ofstream defaultConfig("config.json", std::ios::out);
|
||||||
|
config.configJsonRoot["IP Version"] = 6;
|
||||||
|
config.configJsonRoot["IP"] = "::1";
|
||||||
|
config.configJsonRoot["Port"] = 9010;
|
||||||
|
config.configJsonRoot["LogLevel"] = 1;
|
||||||
|
config.configJsonRoot["LogfileName"] = "Chattering.log";
|
||||||
|
config.configJsonRoot["LogfileSize"] = UINT32_MAX;
|
||||||
|
config.configJsonRoot["LogfileCount"] = 5;
|
||||||
|
defaultConfig << config.configJsonRoot;
|
||||||
|
defaultConfig.close();
|
||||||
|
spdlog::critical("\"config.json\" is missing. Default configuration has been written.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (configfile.is_open())
|
||||||
|
configfile >> config.configJsonRoot;
|
||||||
|
|
||||||
|
config.ipVersion = config.configJsonRoot["IP Version"].asInt();
|
||||||
|
if (config.ipVersion != 4 && config.ipVersion != 6)
|
||||||
|
throw std::runtime_error("Invalid IP Version.");
|
||||||
|
|
||||||
|
config.IP = config.configJsonRoot["IP"].asCString();
|
||||||
|
|
||||||
|
config.Port = config.configJsonRoot["Port"].asInt();
|
||||||
|
if (config.Port < 0 || config.Port > 65535)
|
||||||
|
throw std::runtime_error("Invalid listen port.");
|
||||||
|
|
||||||
|
int ll_ = config.configJsonRoot["LogLevel"].asInt();
|
||||||
|
if (ll_ >= 0 && ll_ < spdlog::level::n_levels)
|
||||||
|
config.logLevel = (spdlog::level::level_enum)ll_;
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid log level.");
|
||||||
|
|
||||||
|
config.logFileName = config.configJsonRoot["LogfileName"].asCString();
|
||||||
|
config.logfileSize = config.configJsonRoot["LogfileSize"].asUInt();
|
||||||
|
config.logfileCount = config.configJsonRoot["LogfileCount"].asUInt();
|
||||||
|
}
|
||||||
|
catch (Json::RuntimeError e) {
|
||||||
|
spdlog::critical(std::string(std::string("[Json Error: ]") + e.what()).c_str());
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
|
#define _CLIENT
|
||||||
#include "Socket/TCPSocket.hpp"
|
#include "Socket/TCPSocket.hpp"
|
||||||
#include "Socket/Address.hpp"
|
#include "Socket/Address.hpp"
|
||||||
#include "Socket/Log.hpp"
|
#include "Socket/Log.hpp"
|
||||||
|
#include "Packet/Packet.hpp"
|
||||||
|
#include "Utils/Snowflake.hpp"
|
||||||
|
#include "ClientManager/ClientManager.hpp"
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
Chattr::TCPSocket sock;
|
Chattr::ClientManager client;
|
||||||
sock.init(AF_INET6);
|
client.init();
|
||||||
|
client.run();
|
||||||
Chattr::Address serveraddr;
|
|
||||||
serveraddr.addr_in6.sin6_family = AF_INET6;
|
|
||||||
inet_pton(AF_INET6, "::1", &serveraddr.addr_in6.sin6_addr);
|
|
||||||
serveraddr.addr_in6.sin6_port = htons(9010);
|
|
||||||
serveraddr.length = sizeof(sockaddr_in6);
|
|
||||||
|
|
||||||
sock.connect(serveraddr);
|
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ FetchContent_MakeAvailable(JSONCPP)
|
|||||||
|
|
||||||
file(GLOB_RECURSE additional_sources CONFIGURE_DEPENDS
|
file(GLOB_RECURSE additional_sources CONFIGURE_DEPENDS
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/Utils/*.cpp"
|
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
@@ -42,11 +42,19 @@ add_executable(${PROJECT_NAME}
|
|||||||
)
|
)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE Microsoft.GSL::GSL)
|
target_link_libraries(${PROJECT_NAME} PRIVATE Microsoft.GSL::GSL)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE spdlog)
|
target_link_libraries(${PROJECT_NAME} PRIVATE spdlog)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE jsoncpp_lib)
|
target_link_libraries(${PROJECT_NAME} PRIVATE jsoncpp_static)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32)
|
target_link_libraries(${PROJECT_NAME} PRIVATE ws2_32)
|
||||||
endif()
|
endif()
|
||||||
|
if(UNIX AND NOT APPLE AND CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
|
BUILD_WITH_INSTALL_RPATH TRUE
|
||||||
|
INSTALL_RPATH "$ORIGIN"
|
||||||
|
SKIP_BUILD_RPATH FALSE
|
||||||
|
BUILD_RPATH "$ORIGIN"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
${CMAKE_CURRENT_SOURCE_DIR}/../include
|
||||||
@@ -56,17 +64,27 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
|
|||||||
|
|
||||||
target_precompile_headers(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../include/precomp.hpp")
|
target_precompile_headers(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../include/precomp.hpp")
|
||||||
|
|
||||||
if(WIN32)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
"$<TARGET_FILE:spdlog>"
|
||||||
"$<TARGET_FILE:spdlog>"
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
COMMENT "Copying spdlog DLL/so to output directory"
|
||||||
COMMENT "Copying spdlog DLL to output directory"
|
)
|
||||||
)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
"$<TARGET_FILE:jsoncpp_static>"
|
||||||
"$<TARGET_FILE:jsoncpp_lib>"
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
COMMENT "Copying jsoncpp DLL/so to output directory"
|
||||||
COMMENT "Copying jsoncpp DLL to output directory"
|
)
|
||||||
)
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
endif()
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"$<TARGET_FILE_DIR:spdlog>"
|
||||||
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
|
COMMENT "Copying spdlog library files and symlinks to output directory"
|
||||||
|
)
|
||||||
|
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
|
"$<TARGET_FILE_DIR:jsoncpp_static>"
|
||||||
|
"$<TARGET_FILE_DIR:${PROJECT_NAME}>"
|
||||||
|
COMMENT "Copying jsoncpp library files and symlinks to output directory"
|
||||||
|
)
|
||||||
91
Server/include/ServerManager/ServerManager.hpp
Normal file
91
Server/include/ServerManager/ServerManager.hpp
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils/ThreadPool.hpp"
|
||||||
|
#include "Utils/ConfigManager.hpp"
|
||||||
|
#include "Socket/IOCP.hpp"
|
||||||
|
#include "Packet/Packet.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
class ServerManager {
|
||||||
|
public:
|
||||||
|
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, config.IP, config.Port);
|
||||||
|
clientAddr_.setType(AF_INET);
|
||||||
|
}
|
||||||
|
else if (config.ipVersion == 6) {
|
||||||
|
listenSock_.init(AF_INET6);
|
||||||
|
serveraddr.set(AF_INET6, config.IP, config.Port);
|
||||||
|
clientAddr_.setType(AF_INET6);
|
||||||
|
}
|
||||||
|
listenSock_.bind(serveraddr);
|
||||||
|
listenSock_.listen(SOMAXCONN);
|
||||||
|
|
||||||
|
spdlog::info("Server listening on [{}]", (std::string)serveraddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
init([this](ThreadPool* thread, IOCPPASSINDATA* data) {
|
||||||
|
this->_IOCPClient(thread, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void processLoginRequestPacket(LoginRequestPacket loginRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomCreateRequestPacket(RoomCreateRequestPacket roomCreateRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomListRequest(RoomListRequestPacket roomListRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomJoinRequestPacket(RoomJoinRequestPacket roomJoinRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processRoomExitRequestPacket(RoomExitRequestPacket roomExitRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processUsersListRequestPacket(UsersListRequestPacket usersListRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processUsersListInRoomRequestPacket(RoomUsersListRequestPacket roomUsersListRequestPacket, IOCPPASSINDATA* data);
|
||||||
|
void processDataPostPacket(DataPostPacket dataPostPacket, IOCPPASSINDATA* data);
|
||||||
|
void processContinuePacket(ContinuePacket continuePacket, IOCPPASSINDATA* data);
|
||||||
|
|
||||||
|
bool registerUser(std::string userName, std::shared_ptr<TCPSocket> sock);
|
||||||
|
void deleteUser(Snowflake UID);
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> getUserList();
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> getUserListInRoom(Snowflake RID);
|
||||||
|
|
||||||
|
Snowflake 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);
|
||||||
|
|
||||||
|
void run();
|
||||||
|
private:
|
||||||
|
ThreadPool threadPool_;
|
||||||
|
IOCP iocp_;
|
||||||
|
|
||||||
|
TCPSocket listenSock_;
|
||||||
|
|
||||||
|
TCPSocket clientSock_;
|
||||||
|
struct Address clientAddr_;
|
||||||
|
|
||||||
|
std::mutex resourceMutex_;
|
||||||
|
|
||||||
|
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<std::shared_ptr<TCPSocket>, Snowflake> userSocket2UID_;
|
||||||
|
|
||||||
|
std::unordered_map<Snowflake, std::string> userNames_;
|
||||||
|
std::unordered_map<std::string, Snowflake> userName2ID_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[2025-04-24 21:34:04.752] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:34:04.752] [Chattering Logger] [info] Waiting for connection...
|
|
||||||
[2025-04-24 21:34:24.926] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:34:24.926] [Chattering Logger] [info] Waiting for connection...
|
|
||||||
[2025-04-24 21:34:39.658] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:34:39.658] [Chattering Logger] [info] Waiting for connection...
|
|
||||||
[2025-04-24 21:34:48.422] [Chattering Logger] [info] Waiting for connection...
|
|
||||||
[2025-04-24 21:34:48.422] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:34:49.422] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:35:32.526] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:35:33.526] [Chattering Logger] [info] thread?
|
|
||||||
[2025-04-24 21:35:33.526] [Chattering Logger] [info] Waiting for connection...
|
|
||||||
561
Server/src/ServerManager/ServerManager.cpp
Normal file
561
Server/src/ServerManager/ServerManager.cpp
Normal file
@@ -0,0 +1,561 @@
|
|||||||
|
#include "ServerManager/ServerManager.hpp"
|
||||||
|
#include "Utils/ConfigManager.hpp"
|
||||||
|
#include "Socket/Log.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
void ServerManager::_IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
if (data->event == IOCPEVENT::QUIT) {
|
||||||
|
if (userSocket2UID_.find(data->socket) != userSocket2UID_.end()) {
|
||||||
|
for (auto room : rooms_) {
|
||||||
|
if (room.second.find(userSocket2UID_[data->socket]) != room.second.end()) {
|
||||||
|
exitRoom(userSocket2UID_[data->socket], room.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteUser(userSocket2UID_[data->socket]);
|
||||||
|
}
|
||||||
|
delete data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chattr::Packet pack;
|
||||||
|
int packetSize = data->transferredbytes;
|
||||||
|
|
||||||
|
memcpy(pack.serialized, data->wsabuf.buf, data->wsabuf.len);
|
||||||
|
|
||||||
|
if (data->event == IOCPEVENT::WRITE && data->transferredbytes >= data->wsabuf.len) {
|
||||||
|
data->event = IOCPEVENT::READ;
|
||||||
|
data->wsabuf.len = 1500;
|
||||||
|
data->IOCPInstance->recv(data, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint16_t packetLength = ::ntohs(pack.__data.packetLength);
|
||||||
|
|
||||||
|
if (data->event == IOCPEVENT::READ && data->transferredbytes < packetLength + 8) {
|
||||||
|
data->IOCPInstance->recv(data, 1);
|
||||||
|
data->wsabuf.len = 1500;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.convToH();
|
||||||
|
PacketSet packetSet = packetParser(pack);
|
||||||
|
pack.convToN();
|
||||||
|
switch (packetSet) {
|
||||||
|
case PacketSet::LOGINREQUEST: {
|
||||||
|
LoginRequestPacket loginRequestPacket;
|
||||||
|
std::memcpy(&loginRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
loginRequestPacket.convToH();
|
||||||
|
processLoginRequestPacket(loginRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMCREATEREQUEST: {
|
||||||
|
RoomCreateRequestPacket roomCreateRequestPacket;
|
||||||
|
std::memcpy(&roomCreateRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
roomCreateRequestPacket.convToH();
|
||||||
|
processRoomCreateRequestPacket(roomCreateRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMLISTREQUEST: {
|
||||||
|
RoomListRequestPacket roomListRequestPacket;
|
||||||
|
std::memcpy(&roomListRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
roomListRequestPacket.convToH();
|
||||||
|
processRoomListRequest(roomListRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMJOINREQUEST: {
|
||||||
|
RoomJoinRequestPacket roomJoinRequestPacket;
|
||||||
|
std::memcpy(&roomJoinRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
roomJoinRequestPacket.convToH();
|
||||||
|
processRoomJoinRequestPacket(roomJoinRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMEXITREQUEST: {
|
||||||
|
RoomExitRequestPacket roomExitRequestPacket;
|
||||||
|
std::memcpy(&roomExitRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
roomExitRequestPacket.convToH();
|
||||||
|
processRoomExitRequestPacket(roomExitRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::USERSLISTREQUEST: {
|
||||||
|
UsersListRequestPacket usersListRequestPacket;
|
||||||
|
std::memcpy(&usersListRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
usersListRequestPacket.convToH();
|
||||||
|
processUsersListRequestPacket(usersListRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::ROOMUSERSLISTREQUEST: {
|
||||||
|
RoomUsersListRequestPacket roomUsersListRequestPacket;
|
||||||
|
std::memcpy(&roomUsersListRequestPacket.serialized, pack.serialized, 8 + packetLength);
|
||||||
|
roomUsersListRequestPacket.convToH();
|
||||||
|
processUsersListInRoomRequestPacket(roomUsersListRequestPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::DATAPOST: {
|
||||||
|
DataPostPacket dataPostPacket;
|
||||||
|
std::memcpy(&dataPostPacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
dataPostPacket.convToH();
|
||||||
|
processDataPostPacket(dataPostPacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::CONTINUE: {
|
||||||
|
ContinuePacket continuePacket;
|
||||||
|
std::memcpy(&continuePacket.serialized, &pack.serialized, 8 + packetLength);
|
||||||
|
continuePacket.convToH();
|
||||||
|
processContinuePacket(continuePacket, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketSet::INVALID:
|
||||||
|
default: {
|
||||||
|
ResponsePacket responsePacket;
|
||||||
|
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, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketSet ServerManager::packetParser(Packet Packet) {
|
||||||
|
if (Packet.__data.packetLength < 0 || Packet.__data.packetLength > 1492)
|
||||||
|
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:
|
||||||
|
case DataType::BINARY:
|
||||||
|
return PacketSet::DATAPOST;
|
||||||
|
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::ROOM_USERS_LIST:
|
||||||
|
return PacketSet::ROOMUSERSLISTREQUEST;
|
||||||
|
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::ROOM_USERS_LIST:
|
||||||
|
return PacketSet::ROOMUSERSLISTRESPONSE;
|
||||||
|
case RequestType::DATA:
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PacketCategory::PACKET_CONTINUE:
|
||||||
|
return PacketSet::CONTINUE;
|
||||||
|
default:
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PacketSet::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processLoginRequestPacket(LoginRequestPacket loginRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
std::string userName(
|
||||||
|
(char*)loginRequestPacket.__data.data,
|
||||||
|
loginRequestPacket.__data.packetLength);
|
||||||
|
if (registerUser(userName, data->socket)) {
|
||||||
|
LoginResponsePacket loginResponsePacket;
|
||||||
|
loginResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
loginResponsePacket.__data.requestType = Chattr::RequestType::LOGIN;
|
||||||
|
loginResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
loginResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(Snowflake);
|
||||||
|
loginResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
Snowflake yourId = userSocket2UID_[data->socket];
|
||||||
|
::memcpy(loginResponsePacket.__data.yourId, &yourId, sizeof(Snowflake));
|
||||||
|
|
||||||
|
loginResponsePacket.convToN();
|
||||||
|
memcpy(data->wsabuf.buf, loginResponsePacket.serialized, 18);
|
||||||
|
data->sendbytes = 18;
|
||||||
|
data->wsabuf.len = 18;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LoginResponsePacket loginResponsePacket;
|
||||||
|
loginResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
loginResponsePacket.__data.requestType = Chattr::RequestType::LOGIN;
|
||||||
|
loginResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
loginResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||||
|
loginResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::BAD_REQUEST;
|
||||||
|
|
||||||
|
loginResponsePacket.convToN();
|
||||||
|
memcpy(data->wsabuf.buf, loginResponsePacket.serialized, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processRoomCreateRequestPacket(RoomCreateRequestPacket roomCreateRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
std::string roomName(
|
||||||
|
(char*)roomCreateRequestPacket.__data.data,
|
||||||
|
roomCreateRequestPacket.__data.packetLength);
|
||||||
|
Snowflake RID = createRoom(roomName);
|
||||||
|
|
||||||
|
RoomCreateResponsePacket roomCreateResponsePacket;
|
||||||
|
roomCreateResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
roomCreateResponsePacket.__data.requestType = Chattr::RequestType::ROOM_CREATE;
|
||||||
|
roomCreateResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
roomCreateResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(Snowflake);
|
||||||
|
roomCreateResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
::memcpy(roomCreateResponsePacket.__data.createdRoomId, &RID, sizeof(Snowflake));
|
||||||
|
|
||||||
|
roomCreateResponsePacket.convToN();
|
||||||
|
memcpy(data->wsabuf.buf, roomCreateResponsePacket.serialized, 18);
|
||||||
|
data->sendbytes = 18;
|
||||||
|
data->wsabuf.len = 18;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processRoomListRequest(RoomListRequestPacket roomListRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
auto roomsList = getRoomList();
|
||||||
|
|
||||||
|
for (auto room : roomsList) {
|
||||||
|
RoomListResponsePacket roomListResponsePacket;
|
||||||
|
roomListResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
roomListResponsePacket.__data.requestType = Chattr::RequestType::ROOM_LIST;
|
||||||
|
roomListResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
roomListResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(std::uint32_t) + sizeof(Snowflake) + room.second.size();
|
||||||
|
roomListResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
roomListResponsePacket.__data.roomCount = roomsList.size();
|
||||||
|
::memcpy(roomListResponsePacket.__data.roomId, &room.first, sizeof(Snowflake));
|
||||||
|
::memcpy(roomListResponsePacket.__data.name, room.second.c_str(), room.second.size());
|
||||||
|
|
||||||
|
int packetLength = roomListResponsePacket.__data.packetLength;
|
||||||
|
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
|
||||||
|
ptr->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
roomListResponsePacket.convToN();
|
||||||
|
memcpy(ptr->wsabuf.buf, roomListResponsePacket.serialized, packetLength + 8);
|
||||||
|
data->sendbytes = packetLength + 8;
|
||||||
|
data->IOCPInstance->send(ptr, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processRoomJoinRequestPacket(RoomJoinRequestPacket roomJoinRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
Snowflake myID, roomID;
|
||||||
|
::memcpy(&myID.snowflake, roomJoinRequestPacket.__data.myId, sizeof(Snowflake));
|
||||||
|
::memcpy(&roomID.snowflake, roomJoinRequestPacket.__data.roomId, sizeof(Snowflake));
|
||||||
|
|
||||||
|
joinRoom(myID, roomID);
|
||||||
|
|
||||||
|
RoomJoinResponsePacket roomJoinResponsePacket;
|
||||||
|
roomJoinResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
roomJoinResponsePacket.__data.requestType = Chattr::RequestType::ROOM_JOIN;
|
||||||
|
roomJoinResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
roomJoinResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||||
|
roomJoinResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
|
||||||
|
int packetLength = roomJoinResponsePacket.__data.packetLength;
|
||||||
|
|
||||||
|
roomJoinResponsePacket.convToN();
|
||||||
|
::memcpy(data->wsabuf.buf, roomJoinResponsePacket.serialized, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processRoomExitRequestPacket(RoomExitRequestPacket roomExitRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
Snowflake myID, roomID;
|
||||||
|
::memcpy(&myID.snowflake, roomExitRequestPacket.__data.myId, sizeof(Snowflake));
|
||||||
|
::memcpy(&roomID.snowflake, roomExitRequestPacket.__data.roomId, sizeof(Snowflake));
|
||||||
|
|
||||||
|
exitRoom(myID, roomID);
|
||||||
|
|
||||||
|
RoomExitResponsePacket roomExitResponsePacket;
|
||||||
|
roomExitResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
roomExitResponsePacket.__data.requestType = Chattr::RequestType::ROOM_EXIT;
|
||||||
|
roomExitResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
roomExitResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
|
||||||
|
roomExitResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
|
||||||
|
int packetLength = roomExitResponsePacket.__data.packetLength;
|
||||||
|
|
||||||
|
roomExitResponsePacket.convToN();
|
||||||
|
::memcpy(data->wsabuf.buf, roomExitResponsePacket.serialized, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processUsersListRequestPacket(UsersListRequestPacket usersListRequestPacket, Chattr::IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
auto usersList = getUserList();
|
||||||
|
|
||||||
|
for (auto user : usersList) {
|
||||||
|
UsersListResponsePacket usersListResponsePacket;
|
||||||
|
usersListResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
usersListResponsePacket.__data.requestType = Chattr::RequestType::USERS_LIST;
|
||||||
|
usersListResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
usersListResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(std::uint32_t) + sizeof(Snowflake) + user.second.size();
|
||||||
|
usersListResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
usersListResponsePacket.__data.usersCount = usersList.size();
|
||||||
|
::memcpy(usersListResponsePacket.__data.userId, &user.first, sizeof(Snowflake));
|
||||||
|
::memcpy(usersListResponsePacket.__data.name, user.second.c_str(), user.second.size());
|
||||||
|
|
||||||
|
int packetLength = usersListResponsePacket.__data.packetLength;
|
||||||
|
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
|
||||||
|
ptr->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
usersListResponsePacket.convToN();
|
||||||
|
memcpy(ptr->wsabuf.buf, usersListResponsePacket.serialized, packetLength + 8);
|
||||||
|
data->sendbytes = packetLength + 8;
|
||||||
|
data->IOCPInstance->send(ptr, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processUsersListInRoomRequestPacket(RoomUsersListRequestPacket roomUsersListRequestPacket, IOCPPASSINDATA *data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
Snowflake roomID;
|
||||||
|
::memcpy(&roomID.snowflake, roomUsersListRequestPacket.__data.roomId, sizeof(Snowflake));
|
||||||
|
auto usersList = getUserListInRoom(roomID);
|
||||||
|
|
||||||
|
for (auto user : usersList) {
|
||||||
|
UsersListResponsePacket usersListResponsePacket;
|
||||||
|
usersListResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
|
||||||
|
usersListResponsePacket.__data.requestType = Chattr::RequestType::USERS_LIST;
|
||||||
|
usersListResponsePacket.__data.dataType = Chattr::DataType::BINARY;
|
||||||
|
usersListResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(std::uint32_t) + sizeof(Snowflake) + user.second.size();
|
||||||
|
usersListResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
|
||||||
|
usersListResponsePacket.__data.usersCount = usersList.size();
|
||||||
|
::memcpy(usersListResponsePacket.__data.userId, &user.first, sizeof(Snowflake));
|
||||||
|
::memcpy(usersListResponsePacket.__data.name, user.second.c_str(), user.second.size());
|
||||||
|
|
||||||
|
int packetLength = usersListResponsePacket.__data.packetLength;
|
||||||
|
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
|
||||||
|
ptr->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
usersListResponsePacket.convToN();
|
||||||
|
memcpy(ptr->wsabuf.buf, usersListResponsePacket.serialized, packetLength + 8);
|
||||||
|
data->sendbytes = packetLength + 8;
|
||||||
|
data->IOCPInstance->send(ptr, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processDataPostPacket(DataPostPacket dataPostPacket, IOCPPASSINDATA *data)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
Snowflake destID;
|
||||||
|
::memcpy(&destID.snowflake, dataPostPacket.__data.destId, sizeof(Snowflake));
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<TCPSocket>> destinationSockets;
|
||||||
|
|
||||||
|
if (userNames_.find(destID) != userNames_.end())
|
||||||
|
destinationSockets.push_back(UID2userSocket_[destID]);
|
||||||
|
else
|
||||||
|
for (auto user : rooms_[destID])
|
||||||
|
destinationSockets.push_back(user.second);
|
||||||
|
|
||||||
|
spdlog::info("Received [{}] from : [{}] to : [{}]",
|
||||||
|
std::string((char*)dataPostPacket.__data.data, dataPostPacket.__data.packetLength - (sizeof(std::uint16_t) * 5)),
|
||||||
|
(std::string)data->socket->remoteAddr,
|
||||||
|
destID.snowflake);
|
||||||
|
|
||||||
|
ResponsePacket responsePacket;
|
||||||
|
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::RES_OK;
|
||||||
|
|
||||||
|
responsePacket.convToN();
|
||||||
|
memcpy(data->wsabuf.buf, responsePacket.serialized, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
|
||||||
|
int packetLength = dataPostPacket.__data.packetLength;
|
||||||
|
|
||||||
|
dataPostPacket.convToN();
|
||||||
|
for (auto dest : destinationSockets) {
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
|
||||||
|
ptr->socket = dest;
|
||||||
|
ptr->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
memcpy(ptr->wsabuf.buf, dataPostPacket.serialized, packetLength + 8);
|
||||||
|
data->sendbytes = packetLength + 8;
|
||||||
|
data->IOCPInstance->send(ptr, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::processContinuePacket(ContinuePacket continuePacket, IOCPPASSINDATA* data) {
|
||||||
|
std::unique_lock<std::mutex> lock(resourceMutex_);
|
||||||
|
Snowflake destID;
|
||||||
|
::memcpy(&destID.snowflake, continuePacket.__data.destId, sizeof(Snowflake));
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<TCPSocket>> destinationSockets;
|
||||||
|
|
||||||
|
if (userNames_.find(destID) != userNames_.end())
|
||||||
|
destinationSockets.push_back(UID2userSocket_[destID]);
|
||||||
|
else
|
||||||
|
for (auto user : rooms_[destID])
|
||||||
|
destinationSockets.push_back(user.second);
|
||||||
|
|
||||||
|
spdlog::info("Received [{}] from : [{}] to : [{}]",
|
||||||
|
std::string((char*)continuePacket.__data.data, continuePacket.__data.packetLength - (sizeof(std::uint16_t) * 5)),
|
||||||
|
(std::string)data->socket->remoteAddr,
|
||||||
|
destID.snowflake);
|
||||||
|
|
||||||
|
ResponsePacket responsePacket;
|
||||||
|
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::RES_OK;
|
||||||
|
|
||||||
|
responsePacket.convToN();
|
||||||
|
memcpy(data->wsabuf.buf, responsePacket.serialized, 10);
|
||||||
|
data->sendbytes = 10;
|
||||||
|
data->wsabuf.len = 10;
|
||||||
|
data->IOCPInstance->send(data, 1, 0);
|
||||||
|
|
||||||
|
int packetLength = continuePacket.__data.packetLength;
|
||||||
|
|
||||||
|
for (auto dest : destinationSockets) {
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
|
||||||
|
ptr->socket = dest;
|
||||||
|
ptr->wsabuf.len = packetLength + 8;
|
||||||
|
|
||||||
|
continuePacket.convToN();
|
||||||
|
memcpy(ptr->wsabuf.buf, continuePacket.serialized, packetLength + 8);
|
||||||
|
data->sendbytes = packetLength + 8;
|
||||||
|
data->IOCPInstance->send(ptr, 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ServerManager::registerUser(std::string userName, std::shared_ptr<TCPSocket> sock) {
|
||||||
|
if (userName2ID_.find(userName) != userName2ID_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Snowflake UID = GenerateID();
|
||||||
|
userNames_[UID] = userName;
|
||||||
|
userName2ID_[userName] = UID;
|
||||||
|
UID2userSocket_[UID] = sock;
|
||||||
|
userSocket2UID_[sock] = UID;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::deleteUser(Snowflake UID) {
|
||||||
|
userNames_.erase(UID);
|
||||||
|
std::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
|
||||||
|
UID2userSocket_.erase(UID);
|
||||||
|
userSocket2UID_.erase(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> ServerManager::getUserList() {
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> userList;
|
||||||
|
userList.reserve(userNames_.size());
|
||||||
|
|
||||||
|
for (auto user : userNames_)
|
||||||
|
userList.push_back(user);
|
||||||
|
|
||||||
|
return userList;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> ServerManager::getUserListInRoom(Snowflake RID) {
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> userList;
|
||||||
|
userList.reserve(rooms_[RID].size());
|
||||||
|
|
||||||
|
for (auto user : rooms_[RID])
|
||||||
|
userList.push_back({ user.first, userNames_[user.first] });
|
||||||
|
|
||||||
|
return userList;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Snowflake ServerManager::createRoom(std::string roomName) {
|
||||||
|
Snowflake RID = GenerateID();
|
||||||
|
roomNames_[RID] = roomName;
|
||||||
|
rooms_[RID] = std::unordered_map<Snowflake, std::shared_ptr<TCPSocket>>();
|
||||||
|
return RID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::deleteRoom(Snowflake RID) {
|
||||||
|
roomNames_.erase(RID);
|
||||||
|
rooms_.erase(RID);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<Snowflake, std::string>> ServerManager::getRoomList() {
|
||||||
|
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::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
|
||||||
|
rooms_[RID][UID] = sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::exitRoom(Snowflake UID, Snowflake RID) {
|
||||||
|
rooms_[RID].erase(UID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerManager::run() {
|
||||||
|
while (true) {
|
||||||
|
spdlog::info("Waiting for connection...");
|
||||||
|
listenSock_.accept(clientSock_, clientAddr_);
|
||||||
|
spdlog::trace("Accepted connection from {}, [{}]", clientSock_.sock, (std::string)clientAddr_);
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA;
|
||||||
|
ptr->socket = std::make_shared<TCPSocket>(std::move(clientSock_));
|
||||||
|
ptr->wsabuf.len = 1500;
|
||||||
|
ptr->IOCPInstance = &iocp_;
|
||||||
|
|
||||||
|
iocp_.registerSocket(ptr);
|
||||||
|
|
||||||
|
int returnData = iocp_.recv(ptr, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
Server/src/Socket/Log.cpp
Normal file
25
Server/src/Socket/Log.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "Socket/Log.hpp"
|
||||||
|
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "spdlog/sinks/msvc_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr::log {
|
||||||
|
|
||||||
|
void setDefaultLogger(spdlog::level::level_enum logLevel, gsl::czstring logFileName, std::uint32_t logFileSize, std::uint32_t logFileCount) {
|
||||||
|
std::vector<spdlog::sink_ptr> sinks;
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFileName, logFileSize, logFileCount, false));
|
||||||
|
#ifdef _WIN32
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
|
||||||
|
#endif
|
||||||
|
auto chatteringLogger = std::make_shared<spdlog::logger>("Chattering Logger", begin(sinks), end(sinks));
|
||||||
|
chatteringLogger->set_level(logLevel);
|
||||||
|
spdlog::set_default_logger(chatteringLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,7 +13,8 @@ ConfigManager::Config ConfigManager::load() {
|
|||||||
if (!configfile.is_open()) {
|
if (!configfile.is_open()) {
|
||||||
std::ofstream defaultConfig("config.json", std::ios::out);
|
std::ofstream defaultConfig("config.json", std::ios::out);
|
||||||
config.configJsonRoot["IP Version"] = 6;
|
config.configJsonRoot["IP Version"] = 6;
|
||||||
config.configJsonRoot["Listen Port"] = 9010;
|
config.configJsonRoot["IP"] = "::";
|
||||||
|
config.configJsonRoot["Port"] = 9010;
|
||||||
config.configJsonRoot["LogLevel"] = 1;
|
config.configJsonRoot["LogLevel"] = 1;
|
||||||
config.configJsonRoot["LogfileName"] = "Chattering.log";
|
config.configJsonRoot["LogfileName"] = "Chattering.log";
|
||||||
config.configJsonRoot["LogfileSize"] = UINT32_MAX;
|
config.configJsonRoot["LogfileSize"] = UINT32_MAX;
|
||||||
@@ -30,8 +31,10 @@ ConfigManager::Config ConfigManager::load() {
|
|||||||
if (config.ipVersion != 4 && config.ipVersion != 6)
|
if (config.ipVersion != 4 && config.ipVersion != 6)
|
||||||
throw std::runtime_error("Invalid IP Version.");
|
throw std::runtime_error("Invalid IP Version.");
|
||||||
|
|
||||||
config.listenPort = config.configJsonRoot["Listen Port"].asInt();
|
config.IP = config.configJsonRoot["IP"].asCString();
|
||||||
if (config.listenPort < 0 || config.listenPort > 65535)
|
|
||||||
|
config.Port = config.configJsonRoot["Port"].asInt();
|
||||||
|
if (config.Port < 0 || config.Port > 65535)
|
||||||
throw std::runtime_error("Invalid listen port.");
|
throw std::runtime_error("Invalid listen port.");
|
||||||
|
|
||||||
int ll_ = config.configJsonRoot["LogLevel"].asInt();
|
int ll_ = config.configJsonRoot["LogLevel"].asInt();
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"IP Version" : 6,
|
|
||||||
"Listen Port" : 9010,
|
|
||||||
"LogLevel" : 1,
|
|
||||||
"LogfileCount" : 5,
|
|
||||||
"LogfileName" : "Chattering.log",
|
|
||||||
"LogfileSize" : 4294967295
|
|
||||||
}
|
|
||||||
@@ -1,50 +1,9 @@
|
|||||||
#include "Socket/TCPSocket.hpp"
|
#include "ServerManager/ServerManager.hpp"
|
||||||
#include "Socket/Log.hpp"
|
|
||||||
#include "Utils/ConfigManager.hpp"
|
|
||||||
#include "Utils/Thread.hpp"
|
|
||||||
|
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
auto config = Chattr::ConfigManager::load();
|
Chattr::ServerManager server;
|
||||||
Chattr::log::setDefaultLogger(config.logLevel, config.logFileName, config.logfileSize, config.logfileCount);
|
server.init();
|
||||||
|
server.run();
|
||||||
struct Chattr::TCPSocket sock;
|
|
||||||
if (config.ipVersion == 4) {
|
|
||||||
sock.init(AF_INET);
|
|
||||||
|
|
||||||
struct Chattr::Address serveraddr;
|
|
||||||
serveraddr.addr_in.sin_family = AF_INET;
|
|
||||||
serveraddr.addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
serveraddr.addr_in.sin_port = htons(config.listenPort);
|
|
||||||
|
|
||||||
sock.bind(&serveraddr.addr_in);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sock.init(AF_INET6);
|
|
||||||
|
|
||||||
struct Chattr::Address serveraddr;
|
|
||||||
serveraddr.addr_in6.sin6_family = AF_INET6;
|
|
||||||
serveraddr.addr_in6.sin6_addr = in6addr_any;
|
|
||||||
serveraddr.addr_in6.sin6_port = htons(config.listenPort);
|
|
||||||
|
|
||||||
sock.bind(&serveraddr.addr_in6);
|
|
||||||
}
|
|
||||||
sock.listen(SOMAXCONN);
|
|
||||||
struct Chattr::TCPSocket clientSock;
|
|
||||||
struct Chattr::Address clientAddr;
|
|
||||||
|
|
||||||
if (config.ipVersion == 4)
|
|
||||||
clientAddr.length = sizeof(sockaddr_in);
|
|
||||||
else
|
|
||||||
clientAddr.length = sizeof(sockaddr_in6);
|
|
||||||
|
|
||||||
Chattr::Thread t1([]() {
|
|
||||||
spdlog::info("thread?");
|
|
||||||
sleep(1);
|
|
||||||
spdlog::info("thread?");
|
|
||||||
});
|
|
||||||
|
|
||||||
spdlog::info("Waiting for connection...");
|
|
||||||
// sock.accept(clientSock, clientAddr);
|
|
||||||
}
|
}
|
||||||
60
impl/Session/Session.cpp
Normal file
60
impl/Session/Session.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//#include "Session/Session.hpp"
|
||||||
|
//#include "Socket/Log.hpp"
|
||||||
|
//#include "precomp.hpp"
|
||||||
|
//#include <thread>
|
||||||
|
//
|
||||||
|
//namespace Chattr {
|
||||||
|
//
|
||||||
|
//Session::Session(Chattr::TCPSocket __sock) {
|
||||||
|
// init(std::move(__sock));
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//Session::~Session() {
|
||||||
|
// destruct();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void Session::init(Chattr::TCPSocket __sock) {
|
||||||
|
// sock_ = std::move(__sock);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//void Session::destruct() {
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//TCPSocket Session::reconnect(Chattr::TCPSocket __sock, int maxTry) {
|
||||||
|
// int retVal = 0;
|
||||||
|
// int backoffTime = 2;
|
||||||
|
//
|
||||||
|
// __sock.destruct();
|
||||||
|
//
|
||||||
|
// spdlog::info("Lost connection.");
|
||||||
|
// while (maxTry--) {
|
||||||
|
// backoffTime *= backoffTime;
|
||||||
|
// spdlog::info("Attempt to reconnect in {}s...", backoffTime);
|
||||||
|
// std::this_thread::sleep_for(std::chrono::seconds(backoffTime));
|
||||||
|
// TCPSocket sock;
|
||||||
|
//
|
||||||
|
// retVal = sock.init(__sock.domain);
|
||||||
|
// if (retVal == INVALID_SOCKET || retVal == SOCKET_ERROR) {
|
||||||
|
// log::error("init()");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// retVal = sock.connect(__sock.remoteAddr);
|
||||||
|
// if (retVal == INVALID_SOCKET || retVal == SOCKET_ERROR) {
|
||||||
|
// log::error("connect()");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return sock;
|
||||||
|
// }
|
||||||
|
// return TCPSocket();
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//int Session::recv(void* __restrict __buf, size_t __n, int __flags) {
|
||||||
|
// return -1;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//int Session::send(const void* __buf, size_t __n, int __flags) {
|
||||||
|
// return -1;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//}
|
||||||
2
impl/Session/SessionManager.cpp
Normal file
2
impl/Session/SessionManager.cpp
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// #include "Session/SessionManager.hpp"
|
||||||
|
#include "precomp.hpp"
|
||||||
109
impl/Socket/Address.cpp
Normal file
109
impl/Socket/Address.cpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "Socket/Address.hpp"
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include <format>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
Address::Address() {
|
||||||
|
zeroFill();
|
||||||
|
}
|
||||||
|
|
||||||
|
Address::Address(int type, gsl::czstring presentationAddr, std::uint16_t port) {
|
||||||
|
set(type, presentationAddr, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::zeroFill() {
|
||||||
|
memset(&addr_in6, 0, sizeof(addr_in6));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::set(int type, gsl::czstring presentationAddr, std::uint16_t port) {
|
||||||
|
zeroFill();
|
||||||
|
|
||||||
|
if (type == AF_INET) {
|
||||||
|
addr_in.sin_family = AF_INET;
|
||||||
|
::inet_pton(AF_INET, presentationAddr, &addr_in.sin_addr);
|
||||||
|
addr_in.sin_port = htons(port);
|
||||||
|
length = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
else if (type == AF_INET6) {
|
||||||
|
addr_in6.sin6_family = AF_INET6;
|
||||||
|
::inet_pton(AF_INET6, presentationAddr, &addr_in6.sin6_addr);
|
||||||
|
addr_in6.sin6_port = htons(port);
|
||||||
|
length = sizeof(sockaddr_in6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::set(int type, in_addr_t addr, std::uint16_t port) {
|
||||||
|
zeroFill();
|
||||||
|
|
||||||
|
if (type == AF_INET) {
|
||||||
|
addr_in.sin_family = AF_INET;
|
||||||
|
addr_in.sin_addr.s_addr = htonl(addr);
|
||||||
|
addr_in.sin_port = htons(port);
|
||||||
|
length = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::set(int type, in_addr addr, std::uint16_t port) {
|
||||||
|
zeroFill();
|
||||||
|
|
||||||
|
if (type == AF_INET) {
|
||||||
|
addr_in.sin_family = AF_INET;
|
||||||
|
addr_in.sin_addr = addr;
|
||||||
|
addr_in.sin_port = htons(port);
|
||||||
|
length = sizeof(sockaddr_in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::set(int type, in6_addr addr, std::uint16_t port) {
|
||||||
|
zeroFill();
|
||||||
|
|
||||||
|
if (type == AF_INET6) {
|
||||||
|
addr_in6.sin6_family = AF_INET6;
|
||||||
|
addr_in6.sin6_addr = addr;
|
||||||
|
addr_in6.sin6_port = htons(port);
|
||||||
|
length = sizeof(sockaddr_in6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Address::setType(int type) {
|
||||||
|
zeroFill();
|
||||||
|
|
||||||
|
if (type == AF_INET)
|
||||||
|
length = sizeof(sockaddr_in);
|
||||||
|
else if (type == AF_INET6)
|
||||||
|
length = sizeof(sockaddr_in6);
|
||||||
|
}
|
||||||
|
|
||||||
|
Address::operator std::string() {
|
||||||
|
std::optional<std::uint16_t> port = getPort();
|
||||||
|
|
||||||
|
if (!port)
|
||||||
|
return std::string();
|
||||||
|
|
||||||
|
if (length == sizeof(addr_in)) {
|
||||||
|
char addrStr[INET_ADDRSTRLEN];
|
||||||
|
::inet_ntop(AF_INET, &addr_in.sin_addr, addrStr, sizeof(addrStr));
|
||||||
|
|
||||||
|
return std::format("{}:{}", addrStr, port.value());
|
||||||
|
}
|
||||||
|
else if (length == sizeof(addr_in6)) {
|
||||||
|
char addrStr[INET6_ADDRSTRLEN];
|
||||||
|
::inet_ntop(AF_INET6, &addr_in6.sin6_addr, addrStr, sizeof(addrStr));
|
||||||
|
|
||||||
|
return std::format("{}:{}", addrStr, port.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uint16_t> Address::getPort() {
|
||||||
|
if (length == sizeof(addr_in))
|
||||||
|
return ntohs(addr_in.sin_port);
|
||||||
|
else if (length == sizeof(addr_in6))
|
||||||
|
return ntohs(addr_in6.sin6_port);
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
impl/Socket/IOCP.cpp
Normal file
72
impl/Socket/IOCP.cpp
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#include "Socket/IOCP.hpp"
|
||||||
|
#include "Utils/ThreadPool.hpp"
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
IOCP::IOCP() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
IOCP::~IOCP() {
|
||||||
|
destruct();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOCP::destruct() {
|
||||||
|
#ifdef __linux__
|
||||||
|
uint64_t u = 1;
|
||||||
|
::write(epollDetroyerFd, &u, sizeof(uint64_t));
|
||||||
|
close(epollfd_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOCP::registerSocket(IOCPPASSINDATA* data) {
|
||||||
|
data->event = IOCPEVENT::READ;
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE returnData = ::CreateIoCompletionPort((HANDLE)data->socket->sock, completionPort_, data->socket->sock, 0);
|
||||||
|
if (returnData == 0)
|
||||||
|
completionPort_ = returnData;
|
||||||
|
#elif __linux__
|
||||||
|
int flags = ::fcntl(data->socket->sock, F_GETFL);
|
||||||
|
flags |= O_NONBLOCK;
|
||||||
|
fcntl(data->socket->sock, F_SETFL, flags);
|
||||||
|
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||||
|
data->sendQueue = std::make_shared<std::queue<IOCPPASSINDATA*>>();
|
||||||
|
ev.data.ptr = data;
|
||||||
|
int rc = epoll_ctl(epollfd_, EPOLL_CTL_ADD, data->socket->sock, &ev);
|
||||||
|
if (rc < 0)
|
||||||
|
log::critical("epoll_ctl()");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int IOCP::recv(Chattr::IOCPPASSINDATA* data, int bufferCount) {
|
||||||
|
data->event = IOCPEVENT::READ;
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD recvbytes = 0, flags = 0;
|
||||||
|
return ::WSARecv(data->socket->sock, &data->wsabuf, bufferCount, &recvbytes, &flags, &data->overlapped, NULL);
|
||||||
|
#elif __linux__
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLONESHOT;
|
||||||
|
ev.data.ptr = data;
|
||||||
|
return ::epoll_ctl(epollfd_, EPOLL_CTL_MOD, data->socket->sock, &ev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int IOCP::send(Chattr::IOCPPASSINDATA* data, int bufferCount, int __flags) { //TCPSocket의 소유권 확인하기. 어디선가 예상치 못하게 socket이 close되었음
|
||||||
|
data->event = IOCPEVENT::WRITE;
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD sendbytes = 0;
|
||||||
|
return ::WSASend(data->socket->sock, &data->wsabuf, bufferCount, &sendbytes, __flags, &data->overlapped, NULL);
|
||||||
|
#elif __linux__
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT;
|
||||||
|
ev.data.ptr = data;
|
||||||
|
data->sendQueue->push(data);
|
||||||
|
return ::epoll_ctl(epollfd_, EPOLL_CTL_MOD, data->socket->sock, &ev);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,18 +10,6 @@
|
|||||||
|
|
||||||
namespace Chattr::log {
|
namespace Chattr::log {
|
||||||
|
|
||||||
void setDefaultLogger(spdlog::level::level_enum logLevel, gsl::czstring logFileName, std::uint32_t logFileSize, std::uint32_t logFileCount) {
|
|
||||||
std::vector<spdlog::sink_ptr> sinks;
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFileName, logFileSize, logFileCount, false));
|
|
||||||
#ifdef _WIN32
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
|
|
||||||
#endif
|
|
||||||
auto chatteringLogger = std::make_shared<spdlog::logger>("Chattering Logger", begin(sinks), end(sinks));
|
|
||||||
chatteringLogger->set_level(logLevel);
|
|
||||||
spdlog::set_default_logger(chatteringLogger);
|
|
||||||
}
|
|
||||||
|
|
||||||
void critical(gsl::czstring msg) {
|
void critical(gsl::czstring msg) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LPSTR msgbuf = nullptr;
|
LPSTR msgbuf = nullptr;
|
||||||
@@ -55,11 +43,11 @@ void error(gsl::czstring msg) {
|
|||||||
(LPSTR)&msgbuf,
|
(LPSTR)&msgbuf,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL);
|
||||||
spdlog::critical("[{}] {}", msg, msgbuf);
|
spdlog::error("[{}] {}", msg, msgbuf);
|
||||||
LocalFree(msgbuf);
|
LocalFree(msgbuf);
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
gsl::czstring msgbuf = strerror(errno);
|
gsl::czstring msgbuf = strerror(errno);
|
||||||
spdlog::critical("[{}] {}", msg, msgbuf);
|
spdlog::error("[{}] {}", msg, msgbuf);
|
||||||
#else
|
#else
|
||||||
#error "이 플랫폼은 지원되지 않습니다."
|
#error "이 플랫폼은 지원되지 않습니다."
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,104 +4,110 @@
|
|||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
Socket::Socket() {
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSADATA wsa;
|
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
||||||
log::critical("WSAStartup()");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Socket::Socket(int domain, int type, int protocol) {
|
Socket::Socket(int domain, int type, int protocol) {
|
||||||
#ifdef _WIN32
|
|
||||||
WSADATA wsa;
|
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
||||||
log::critical("WSAStartup()");
|
|
||||||
#endif
|
|
||||||
init(domain, type, protocol);
|
init(domain, type, protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::~Socket() {
|
Socket::~Socket() {
|
||||||
if (!valid_)
|
destruct();
|
||||||
return;
|
|
||||||
#ifdef _WIN32
|
|
||||||
closesocket(sock_);
|
|
||||||
WSACleanup();
|
|
||||||
#elif __linux__
|
|
||||||
::close(sock_);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::init(int domain, int type, int protocol) {
|
int Socket::init(int domain, int type, int protocol) {
|
||||||
if (domain == AF_INET)
|
this->domain = domain;
|
||||||
bindAddr.length = sizeof(sockaddr_in);
|
|
||||||
else if (domain == AF_INET6)
|
|
||||||
bindAddr.length = sizeof(sockaddr_in6);
|
|
||||||
|
|
||||||
sock_ = ::socket(domain, type, protocol);
|
sock = ::socket(domain, type, protocol);
|
||||||
if (sock_ == INVALID_SOCKET)
|
if (sock == INVALID_SOCKET)
|
||||||
log::critical("socket()");
|
log::critical("socket()");
|
||||||
|
|
||||||
valid_ = true;
|
valid_ = true;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Socket::operator SOCKET() const {
|
void Socket::destruct() {
|
||||||
if (valid_)
|
if (!valid_)
|
||||||
return sock_;
|
return;
|
||||||
|
#ifdef _WIN32
|
||||||
|
::closesocket(sock);
|
||||||
|
#elif __linux__
|
||||||
|
::close(sock);
|
||||||
|
#endif
|
||||||
|
valid_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket::operator SOCKET() {
|
||||||
|
if (valid_) {
|
||||||
|
valid_ = false;
|
||||||
|
return sock;
|
||||||
|
}
|
||||||
spdlog::critical("No valid socket created.");
|
spdlog::critical("No valid socket created.");
|
||||||
return INVALID_SOCKET;
|
return INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::move(const SOCKET __sock) {
|
void Socket::set(const SOCKET __sock, int __domain) {
|
||||||
if (__sock == INVALID_SOCKET)
|
if (__sock == INVALID_SOCKET)
|
||||||
log::critical("socket()");
|
log::critical("socket()");
|
||||||
|
|
||||||
if (valid_) {
|
destruct();
|
||||||
#ifdef _WIN32
|
|
||||||
closesocket(sock_);
|
|
||||||
WSACleanup();
|
|
||||||
#elif __linux__
|
|
||||||
::close(sock_);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSADATA wsa;
|
|
||||||
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
|
||||||
log::critical("WSAStartup()");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
sock_ = __sock;
|
sock = __sock;
|
||||||
valid_ = true;
|
valid_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Socket::bind(sockaddr *__addr) {
|
int Chattr::Socket::setsockopt(int level, int optname, const char* optval, int optlen) {
|
||||||
bind((struct sockaddr *)__addr, sizeof(sockaddr));
|
return ::setsockopt(sock, level, optname, optval, optlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::bind(sockaddr *__addr, socklen_t __len) {
|
int Socket::bind(Address __addr) {
|
||||||
bindAddr.length = __len;
|
bindAddr = __addr;
|
||||||
std::memcpy(&bindAddr, __addr, __len);
|
int retVal = ::bind(sock, &__addr.addr, __addr.length);
|
||||||
int retVal = ::bind(sock_, __addr, __len);
|
|
||||||
if (retVal == INVALID_SOCKET)
|
if (retVal == INVALID_SOCKET)
|
||||||
log::critical("bind()");
|
log::critical("bind()");
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::bind(sockaddr_in *__addr) {
|
int Socket::recvfrom(void *__restrict __buf, size_t __n, int __flags, struct Address& __addr) {
|
||||||
bind((struct sockaddr *)__addr, sizeof(sockaddr_in));
|
std::lock_guard<std::mutex> lock(readMutex);
|
||||||
|
int retVal = ::recvfrom(sock, (char*)__buf, __n, __flags, &__addr.addr, &__addr.length);
|
||||||
|
if (retVal == SOCKET_ERROR)
|
||||||
|
log::error("recvfrom()");
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::bind(sockaddr_in *__addr, socklen_t __len) {
|
int Socket::sendto(const void *__buf, size_t __n, int __flags, struct Address __addr) {
|
||||||
bind((struct sockaddr *)__addr, __len);
|
std::lock_guard<std::mutex> lock(writeMutex);
|
||||||
|
int retVal = ::sendto(sock, (char*)__buf, __n, __flags, &__addr.addr, __addr.length);
|
||||||
|
if (retVal == SOCKET_ERROR)
|
||||||
|
log::error("sendto()");
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::bind(sockaddr_in6 *__addr) {
|
Socket::Socket(const Socket& other_) {
|
||||||
bind((struct sockaddr *)__addr, sizeof(sockaddr_in6));
|
memcpy(this, &other_, sizeof(Socket));
|
||||||
|
valid_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Socket::bind(sockaddr_in6 *__addr, socklen_t __len) {
|
Socket::Socket(Socket &&other_) noexcept {
|
||||||
bind((struct sockaddr *)__addr, __len);
|
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));
|
||||||
|
valid_ = true;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,29 +4,47 @@
|
|||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
void TCPSocket::init(int domain) {
|
int TCPSocket::init(int domain) {
|
||||||
init(domain, SOCK_STREAM, 0);
|
return init(domain, SOCK_STREAM, 0);
|
||||||
valid_ = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSocket::listen(int __n) {
|
int TCPSocket::listen(int __n) {
|
||||||
int retVal = ::listen(sock_, __n);
|
int retVal = ::listen(sock, __n);
|
||||||
if (retVal == INVALID_SOCKET)
|
if (retVal == INVALID_SOCKET)
|
||||||
log::critical("listen()");
|
log::error("listen()");
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSocket::accept(TCPSocket& newSock, Address& __addr) {
|
void TCPSocket::accept(TCPSocket& newSock, Address& __addr) {
|
||||||
newSock.move(::accept(sock_, &__addr.addr, &__addr.length));
|
newSock.set(::accept(sock, &__addr.addr, &__addr.length), domain);
|
||||||
if (newSock == INVALID_SOCKET)
|
memcpy(&newSock.remoteAddr, &__addr, sizeof(Chattr::Address));
|
||||||
log::critical("accept()");
|
if (newSock.sock == INVALID_SOCKET)
|
||||||
|
log::error("accept()");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TCPSocket::connect(Chattr::Address serveraddr) {
|
int TCPSocket::connect(Address& serveraddr) {
|
||||||
int retVal = ::connect(sock_, (struct sockaddr *)&serveraddr, serveraddr.length);
|
int retVal = ::connect(sock, (struct sockaddr *)&serveraddr.addr, serveraddr.length);
|
||||||
|
memcpy(&remoteAddr, &serveraddr, sizeof(Chattr::Address));
|
||||||
if (retVal == INVALID_SOCKET)
|
if (retVal == INVALID_SOCKET)
|
||||||
log::critical("connect()");
|
log::error("connect()");
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TCPSocket::recv(void *__restrict __buf, size_t __n, int __flags) {
|
||||||
|
int retVal = ::recv(sock, (char *)__buf, __n, __flags);
|
||||||
|
if (retVal == SOCKET_ERROR) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
return retVal;
|
||||||
|
log::error("recv()");
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
int TCPSocket::send(const void *__buf, size_t __n, int __flags) {
|
||||||
|
int retVal = ::send(sock, (char*)__buf, __n, __flags);
|
||||||
|
if (retVal == SOCKET_ERROR)
|
||||||
|
log::error("send()");
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
21
impl/Socket/WSAManager.cpp
Normal file
21
impl/Socket/WSAManager.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "Socket/WSAManager.hpp"
|
||||||
|
#include "Socket/Log.hpp"
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
WSAManager::WSAManager() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsa;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
|
||||||
|
log::critical("WSAStartup()");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
WSAManager::~WSAManager() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
impl/Utils/GenerateID.cpp
Normal file
35
impl/Utils/GenerateID.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "Utils/Snowflake.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
Snowflake GenerateID() {
|
||||||
|
static struct EpochInitializer {
|
||||||
|
EpochInitializer() {
|
||||||
|
EPOCH = std::chrono::system_clock::now();
|
||||||
|
}
|
||||||
|
std::chrono::system_clock::time_point EPOCH;
|
||||||
|
} epochInitializer;
|
||||||
|
|
||||||
|
static std::mutex snowflakeGenerateMutex_;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(snowflakeGenerateMutex_);
|
||||||
|
|
||||||
|
std::size_t tid;
|
||||||
|
#ifdef _WIN32
|
||||||
|
tid = static_cast<std::size_t>(GetCurrentThreadId());
|
||||||
|
#elif __linux__
|
||||||
|
tid = static_cast<std::size_t>(pthread_self());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
thread_local static int sequence = 0;
|
||||||
|
Snowflake id = {};
|
||||||
|
|
||||||
|
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - epochInitializer.EPOCH);
|
||||||
|
id.timestamp = timestamp.count();
|
||||||
|
id.instance = tid;
|
||||||
|
id.sequence = sequence++;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
81
impl/Utils/StringTokenizer.cpp
Normal file
81
impl/Utils/StringTokenizer.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "Utils/StringTokenizer.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
StringTokenizer::StringTokenizer(gsl::czstring string, std::uint32_t size) {
|
||||||
|
set(string, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringTokenizer::set(gsl::czstring string, std::uint32_t size) {
|
||||||
|
int length = 0;
|
||||||
|
bool token = false;
|
||||||
|
bool quote = false, doubleQuote = false;
|
||||||
|
|
||||||
|
for (std::uint32_t i = 0; i < size; i++) {
|
||||||
|
if (string[i] == ' ' && token != true)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (token == false)
|
||||||
|
tokens.push(&string[i]);
|
||||||
|
token = true;
|
||||||
|
|
||||||
|
switch (string[i]) {
|
||||||
|
case '\"':
|
||||||
|
doubleQuote = !doubleQuote;
|
||||||
|
length++;
|
||||||
|
break;
|
||||||
|
case '\'':
|
||||||
|
quote = !quote;
|
||||||
|
length++;
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
case '\n':
|
||||||
|
case ' ':
|
||||||
|
if (quote || doubleQuote) {
|
||||||
|
length++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tokens_length.push(length);
|
||||||
|
length = 0;
|
||||||
|
token = false;
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
tokens_length.push(length);
|
||||||
|
length = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote || doubleQuote) {
|
||||||
|
spdlog::error("quote or doublequote is not closed");
|
||||||
|
tokens = std::queue<gsl::czstring>();
|
||||||
|
tokens_length = std::queue<std::uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length != 0)
|
||||||
|
tokens_length.push(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> StringTokenizer::get() {
|
||||||
|
std::vector<std::string> retValue;
|
||||||
|
|
||||||
|
while (!tokens.empty()) {
|
||||||
|
if (tokens_length.front() != 0)
|
||||||
|
retValue.push_back(std::string(tokens.front(), tokens_length.front()));
|
||||||
|
tokens.pop();
|
||||||
|
tokens_length.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!tokens_length.empty())
|
||||||
|
tokens_length.pop();
|
||||||
|
|
||||||
|
return retValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringTokenizer::operator std::vector<std::string>() {
|
||||||
|
return get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,20 +1,26 @@
|
|||||||
#include "Utils/Thread.hpp"
|
#include "Utils/Thread.hpp"
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
|
Thread::Thread(Thread&& other) noexcept {
|
||||||
|
other.detach();
|
||||||
|
memcpy(this, &other, sizeof(Thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread& Thread::operator=(Thread&& other) noexcept {
|
||||||
|
other.detach();
|
||||||
|
memcpy(this, &other, sizeof(Thread));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
Thread::~Thread() {
|
Thread::~Thread() {
|
||||||
if (!detached)
|
if (!detached) {
|
||||||
join();
|
spdlog::critical("There is not joined thread");
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::join() {
|
void Thread::detach() {
|
||||||
#ifdef _WIN32
|
|
||||||
WaitForSingleObject(handle_, INFINITE);
|
|
||||||
#elif __linux__
|
|
||||||
pthread_join(handle_, NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void Thread::deatch() {
|
|
||||||
detached = true;
|
detached = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
89
impl/Utils/ThreadPool.cpp
Normal file
89
impl/Utils/ThreadPool.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "Utils/ThreadPool.hpp"
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
ThreadPool::ThreadPool() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::ThreadPool(std::uint32_t numThreads) {
|
||||||
|
init(numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPool::~ThreadPool() {
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::init(std::uint32_t numThreads) {
|
||||||
|
int numCPU = numThreads;
|
||||||
|
if (numThreads == 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
SYSTEM_INFO sysinfo;
|
||||||
|
GetSystemInfo(&sysinfo);
|
||||||
|
numCPU = sysinfo.dwNumberOfProcessors;
|
||||||
|
#elif __linux__
|
||||||
|
numCPU = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
#endif
|
||||||
|
spdlog::info("Auto-detected cpu count: {}", numCPU);
|
||||||
|
if (numCPU == 1 || numCPU == 2) {
|
||||||
|
numCPU = 4;
|
||||||
|
spdlog::info("Set ThreadPool Worker count to: {} due to program to oprate concurrently", numCPU);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
spdlog::info("Set ThreadPool Worker count to: {}", numCPU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadCount = numCPU;
|
||||||
|
workers_.reserve(numCPU);
|
||||||
|
|
||||||
|
while (numCPU--)
|
||||||
|
workers_.push_back([this]() {
|
||||||
|
this->Worker();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::terminate() {
|
||||||
|
terminate_ = true;
|
||||||
|
jobQueueCV_.notify_all();
|
||||||
|
|
||||||
|
spdlog::debug("waiting for threads to end their jobs...");
|
||||||
|
for (auto& t : workers_)
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::respawnWorker(std::uint32_t numThreads) {
|
||||||
|
terminate();
|
||||||
|
terminate_ = false;
|
||||||
|
init(numThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ThreadPool::Worker() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD pid = GetCurrentThreadId();
|
||||||
|
#elif __linux__
|
||||||
|
pthread_t pid = pthread_self();
|
||||||
|
#endif
|
||||||
|
spdlog::trace("ThreadPool Worker : {} up", pid);
|
||||||
|
while (!terminate_) {
|
||||||
|
std::unique_lock<std::mutex> lock(jobQueueMutex);
|
||||||
|
jobQueueCV_.wait(lock, [this]() { return !this->jobs_.empty() || terminate_; });
|
||||||
|
if (this->jobs_.empty() || terminate_) {
|
||||||
|
jobs_ = std::queue<std::packaged_task<void()>>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (this->jobs_.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto job = std::move(jobs_.front());
|
||||||
|
jobs_.pop();
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
spdlog::trace("ThreadPool Worker : {} Executing a job", pid);
|
||||||
|
job();
|
||||||
|
}
|
||||||
|
spdlog::trace("ThreadPool Worker : {} down", pid);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
402
include/Packet/Packet.hpp
Normal file
402
include/Packet/Packet.hpp
Normal file
@@ -0,0 +1,402 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils/Snowflake.hpp"
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
enum class PacketSet {
|
||||||
|
LOGINREQUEST,
|
||||||
|
ROOMCREATEREQUEST,
|
||||||
|
ROOMLISTREQUEST,
|
||||||
|
ROOMJOINREQUEST,
|
||||||
|
ROOMEXITREQUEST,
|
||||||
|
USERSLISTREQUEST,
|
||||||
|
ROOMUSERSLISTREQUEST,
|
||||||
|
DATAPOST,
|
||||||
|
CONTINUE,
|
||||||
|
RESPONSE,
|
||||||
|
LOGINRESPONSE,
|
||||||
|
ROOMCREATERESPONSE,
|
||||||
|
ROOMLISTRESPONSE,
|
||||||
|
ROOMJOINRESPONSE,
|
||||||
|
ROOMEXITRESPONSE,
|
||||||
|
USERSLISTRESPONSE,
|
||||||
|
ROOMUSERSLISTRESPONSE,
|
||||||
|
INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PacketCategory : std::uint8_t {
|
||||||
|
PACKET_POST,
|
||||||
|
PACKET_REQUEST,
|
||||||
|
PACKET_RESPONSE,
|
||||||
|
PACKET_CONTINUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class RequestType : std::uint8_t {
|
||||||
|
LOGIN,
|
||||||
|
ROOM_CREATE,
|
||||||
|
ROOM_LIST,
|
||||||
|
ROOM_JOIN,
|
||||||
|
ROOM_EXIT,
|
||||||
|
USERS_LIST,
|
||||||
|
ROOM_USERS_LIST,
|
||||||
|
DATA
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MOREFRAG 0b00000001
|
||||||
|
|
||||||
|
enum class DataType : std::uint8_t {
|
||||||
|
TEXT,
|
||||||
|
BINARY
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
std::uint8_t data[1495];
|
||||||
|
} __data;
|
||||||
|
std::uint8_t serialized[1500] = "";
|
||||||
|
};
|
||||||
|
std::uint8_t* convToN() {
|
||||||
|
__data.packetLength = ::htons(__data.packetLength);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) LoginRequestPacket : public Packet {};
|
||||||
|
|
||||||
|
class alignas(4) RoomCreateRequestPacket : public Packet {};
|
||||||
|
|
||||||
|
class alignas(4) RoomListRequestPacket : public Packet {};
|
||||||
|
|
||||||
|
class alignas(4) RoomJoinRequestPacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
std::uint16_t myId[4];
|
||||||
|
std::uint16_t roomId[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.myId[i] = ::htons(__data.myId[i]);
|
||||||
|
__data.roomId[i] = ::htons(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
__data.myId[i] = ::ntohs(__data.myId[i]);
|
||||||
|
__data.roomId[i] = ::ntohs(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) RoomExitRequestPacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
std::uint16_t myId[4];
|
||||||
|
std::uint16_t roomId[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.myId[i] = ::htons(__data.myId[i]);
|
||||||
|
__data.roomId[i] = ::htons(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
__data.myId[i] = ::ntohs(__data.myId[i]);
|
||||||
|
__data.roomId[i] = ::ntohs(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) UsersListRequestPacket : public Packet {};
|
||||||
|
|
||||||
|
class alignas(4) RoomUsersListRequestPacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
std::uint16_t roomId[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.roomId[i] = ::htons(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
__data.roomId[i] = ::ntohs(__data.roomId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) DataPostPacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
std::uint16_t sourceId[4];
|
||||||
|
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.sourceId[i] = ::htons(__data.sourceId[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.sourceId[i] = ::ntohs(__data.sourceId[i]);
|
||||||
|
__data.destId[i] = ::ntohs(__data.destId[i]);
|
||||||
|
}
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) ContinuePacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
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 {
|
||||||
|
RES_OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
LENGTH_REQUIRED = 411,
|
||||||
|
IAM_A_TEAPOT = 418,
|
||||||
|
TOO_MANY_REQUESTS = 429,
|
||||||
|
INTERNAL_SERVER_ERROR = 500
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) ResponsePacket : public Packet {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
ResponseStatusCode responseStatusCode;
|
||||||
|
std::uint8_t data[1493];
|
||||||
|
} __data;
|
||||||
|
std::uint8_t serialized[1500] = "";
|
||||||
|
};
|
||||||
|
std::uint8_t* convToN() {
|
||||||
|
__data.packetLength = ::htons(__data.packetLength);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::htons((std::uint16_t)__data.responseStatusCode);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::ntohs((std::uint16_t)__data.responseStatusCode);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) LoginResponsePacket : public ResponsePacket {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
PacketCategory packetType;
|
||||||
|
RequestType requestType;
|
||||||
|
DataType dataType;
|
||||||
|
std::uint16_t packetLength;
|
||||||
|
ResponseStatusCode responseStatusCode;
|
||||||
|
std::uint16_t yourId[4];
|
||||||
|
std::uint8_t data[];
|
||||||
|
} __data;
|
||||||
|
std::uint8_t serialized[1500] = "";
|
||||||
|
};
|
||||||
|
std::uint8_t* convToN() {
|
||||||
|
__data.packetLength = ::htons(__data.packetLength);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::htons((std::uint16_t)__data.responseStatusCode);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
__data.yourId[i] = ::htons(__data.yourId[i]);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
std::uint8_t* convToH() {
|
||||||
|
__data.packetLength = ::ntohs(__data.packetLength);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::ntohs((std::uint16_t)__data.responseStatusCode);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
__data.yourId[i] = ::ntohs(__data.yourId[i]);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class alignas(4) RoomCreateResponsePacket : public ResponsePacket {
|
||||||
|
public:
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
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);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::htons((std::uint16_t)__data.responseStatusCode);
|
||||||
|
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);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::ntohs((std::uint16_t)__data.responseStatusCode);
|
||||||
|
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.responseStatusCode = (ResponseStatusCode)::htons((std::uint16_t)__data.responseStatusCode);
|
||||||
|
__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.responseStatusCode = (ResponseStatusCode)::ntohs((std::uint16_t)__data.responseStatusCode);
|
||||||
|
__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);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::htons((std::uint16_t)__data.responseStatusCode);
|
||||||
|
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);
|
||||||
|
__data.responseStatusCode = (ResponseStatusCode)::ntohs((std::uint16_t)__data.responseStatusCode);
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
__data.userId[i] = ::ntohs(__data.userId[i]);
|
||||||
|
return serialized;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "precomp.hpp"
|
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
struct Address {
|
struct Address {
|
||||||
Address() {
|
Address();
|
||||||
memset(&addr_in6, 0, sizeof(addr_in6));
|
Address(int type, gsl::czstring presentationAddr, std::uint16_t port);
|
||||||
}
|
|
||||||
|
void zeroFill();
|
||||||
|
void set(int type, gsl::czstring presentationAddr, std::uint16_t port);
|
||||||
|
void set(int type, in_addr_t addr, std::uint16_t port);
|
||||||
|
void set(int type, in_addr addr, std::uint16_t port);
|
||||||
|
void set(int type, in6_addr addr, std::uint16_t port);
|
||||||
|
void setType(int type);
|
||||||
|
|
||||||
|
operator std::string();
|
||||||
|
std::optional<std::uint16_t> getPort();
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct sockaddr addr;
|
struct sockaddr addr;
|
||||||
|
|||||||
364
include/Socket/IOCP.hpp
Normal file
364
include/Socket/IOCP.hpp
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils/ThreadPool.hpp"
|
||||||
|
#include "Socket/WSAManager.hpp"
|
||||||
|
#include "Socket/TCPSocket.hpp"
|
||||||
|
#include "Socket/Log.hpp"
|
||||||
|
#include "Packet/Packet.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
typedef struct _OVERLAPPED {
|
||||||
|
char dummy;
|
||||||
|
} OVERLAPPED;
|
||||||
|
|
||||||
|
typedef struct __WSABUF {
|
||||||
|
std::uint32_t len;
|
||||||
|
char *buf;
|
||||||
|
} WSABUF;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
class IOCP;
|
||||||
|
|
||||||
|
enum class IOCPEVENT {
|
||||||
|
QUIT,
|
||||||
|
READ,
|
||||||
|
WRITE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IOCPPASSINDATA {
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
IOCPEVENT event;
|
||||||
|
std::shared_ptr<TCPSocket> socket;
|
||||||
|
char buf[1501];
|
||||||
|
std::uint32_t recvbytes;
|
||||||
|
std::uint32_t sendbytes;
|
||||||
|
std::uint32_t transferredbytes;
|
||||||
|
WSABUF wsabuf;
|
||||||
|
IOCP* IOCPInstance;
|
||||||
|
#ifdef __linux__
|
||||||
|
std::shared_ptr<std::queue<IOCPPASSINDATA*>> sendQueue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IOCPPASSINDATA() {
|
||||||
|
std::memset(&overlapped, 0, sizeof(overlapped));
|
||||||
|
event = IOCPEVENT::QUIT;
|
||||||
|
socket = nullptr;
|
||||||
|
recvbytes = 0;
|
||||||
|
sendbytes = 0;
|
||||||
|
transferredbytes = 0;
|
||||||
|
wsabuf.len = 1500;
|
||||||
|
wsabuf.buf = this->buf;
|
||||||
|
IOCPInstance = nullptr;
|
||||||
|
}
|
||||||
|
IOCPPASSINDATA(const IOCPPASSINDATA& other)
|
||||||
|
: event(other.event),
|
||||||
|
socket(other.socket),
|
||||||
|
transferredbytes(other.transferredbytes),
|
||||||
|
wsabuf(other.wsabuf),
|
||||||
|
IOCPInstance(other.IOCPInstance)
|
||||||
|
#ifdef __linux__
|
||||||
|
, sendQueue(other.sendQueue)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
recvbytes = 0;
|
||||||
|
sendbytes = 0;
|
||||||
|
wsabuf.buf = this->buf;
|
||||||
|
// buf는 memcpy로 복사
|
||||||
|
std::memcpy(buf, other.buf, sizeof(buf));
|
||||||
|
// overlapped는 0으로 초기화 (복사하면 안 됨)
|
||||||
|
std::memset(&overlapped, 0, sizeof(overlapped));
|
||||||
|
}
|
||||||
|
|
||||||
|
~IOCPPASSINDATA() = default;
|
||||||
|
|
||||||
|
// 복사 대입 연산자
|
||||||
|
IOCPPASSINDATA& operator=(const IOCPPASSINDATA& other) {
|
||||||
|
if (this != &other) {
|
||||||
|
event = other.event;
|
||||||
|
socket = other.socket;
|
||||||
|
recvbytes = 0;
|
||||||
|
sendbytes = 0;
|
||||||
|
transferredbytes = other.transferredbytes;
|
||||||
|
wsabuf.buf = this->buf;
|
||||||
|
IOCPInstance = other.IOCPInstance;
|
||||||
|
#ifdef __linux__
|
||||||
|
sendQueue = other.sendQueue;
|
||||||
|
#endif
|
||||||
|
std::memcpy(buf, other.buf, sizeof(buf));
|
||||||
|
std::memset(&overlapped, 0, sizeof(overlapped));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IOCP {
|
||||||
|
public:
|
||||||
|
#ifdef _WIN32
|
||||||
|
static void iocpWather(ThreadPool* threadPool, HANDLE completionPort_, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
|
||||||
|
DWORD tid = GetCurrentThreadId();
|
||||||
|
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) {
|
||||||
|
data->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(data->socket->remoteAddr));
|
||||||
|
threadPool->enqueueJob(callback, data);
|
||||||
|
threadPool->enqueueJob(iocpWather, completionPort_, callback);
|
||||||
|
// delete data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data->transferredbytes = cbTransfrred;
|
||||||
|
threadPool->enqueueJob(callback, data);
|
||||||
|
threadPool->enqueueJob(iocpWather, completionPort_, callback);
|
||||||
|
};
|
||||||
|
#elif __linux__
|
||||||
|
static void socketReader(ThreadPool* threadPool, epoll_event event, int epollfd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
|
||||||
|
pthread_t tid = pthread_self();
|
||||||
|
|
||||||
|
if (event.data.ptr == nullptr) {
|
||||||
|
spdlog::error("invalid call on {}", tid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOCPPASSINDATA* rootIocpData = (IOCPPASSINDATA*)event.data.ptr;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(rootIocpData->socket->readMutex);
|
||||||
|
while (true) {
|
||||||
|
char peekBuffer[1];
|
||||||
|
int rc = rootIocpData->socket->recv(peekBuffer, 1, MSG_PEEK);
|
||||||
|
if (rc > 0);
|
||||||
|
else if (rc == 0) {
|
||||||
|
rootIocpData->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(rootIocpData->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, rootIocpData->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, rootIocpData);
|
||||||
|
// delete rootIocpData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
spdlog::trace("No data to read on {}", tid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rootIocpData->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(rootIocpData->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, rootIocpData->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, rootIocpData);
|
||||||
|
// delete rootIocpData;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*rootIocpData);
|
||||||
|
ptr->wsabuf.len = 1500;
|
||||||
|
|
||||||
|
int redSize = 0;
|
||||||
|
int headerSize = 8;
|
||||||
|
int totalRedSize = 0;
|
||||||
|
|
||||||
|
while (totalRedSize < headerSize) {
|
||||||
|
redSize = ptr->socket->recv(ptr->buf + totalRedSize, headerSize - totalRedSize, 0);
|
||||||
|
|
||||||
|
if (redSize == SOCKET_ERROR) {
|
||||||
|
ptr->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(ptr->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, ptr->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, ptr);
|
||||||
|
// delete ptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (redSize == 0) {
|
||||||
|
ptr->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(ptr->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, ptr->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, ptr);
|
||||||
|
// delete ptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
totalRedSize += redSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet packet;
|
||||||
|
::memcpy(packet.serialized, ptr->buf, headerSize);
|
||||||
|
|
||||||
|
redSize = 0;
|
||||||
|
int dataLength = ntohs(packet.__data.packetLength);
|
||||||
|
|
||||||
|
while (totalRedSize < dataLength + headerSize) {
|
||||||
|
redSize = ptr->socket->recv(ptr->buf + totalRedSize, dataLength + headerSize - totalRedSize, 0);
|
||||||
|
|
||||||
|
if (redSize == SOCKET_ERROR) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
spdlog::trace("No data to read on {}", tid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ptr->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(ptr->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, ptr->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, ptr);
|
||||||
|
// delete ptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (redSize == 0) {
|
||||||
|
ptr->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(ptr->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, ptr->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, ptr);
|
||||||
|
// delete ptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
totalRedSize += redSize;
|
||||||
|
}
|
||||||
|
ptr->transferredbytes = totalRedSize;
|
||||||
|
threadPool->enqueueJob(callback, ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static void socketWriter(ThreadPool* threadPool, epoll_event event, int epollfd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
|
||||||
|
pthread_t tid = pthread_self();
|
||||||
|
|
||||||
|
if (event.data.ptr == nullptr) {
|
||||||
|
spdlog::error("invalid call on {}", tid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOCPPASSINDATA* rootIocpData = (IOCPPASSINDATA*)event.data.ptr;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(rootIocpData->socket->writeMutex);
|
||||||
|
while (!rootIocpData->sendQueue->empty()) {
|
||||||
|
IOCPPASSINDATA* data = rootIocpData->sendQueue->front();
|
||||||
|
rootIocpData->sendQueue->pop();
|
||||||
|
|
||||||
|
if (data == nullptr) {
|
||||||
|
spdlog::error("invalid call on {}", tid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int packetSize = data->wsabuf.len;
|
||||||
|
int totalSentSize = 0;
|
||||||
|
int sentSize = 0;
|
||||||
|
|
||||||
|
spdlog::trace("Sending to: [{}]", (std::string)data->socket->remoteAddr);
|
||||||
|
|
||||||
|
while (totalSentSize < packetSize) {
|
||||||
|
sentSize = data->socket->send(data->buf + totalSentSize, packetSize - totalSentSize, 0);
|
||||||
|
|
||||||
|
if (sentSize == SOCKET_ERROR) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
spdlog::warn("buffer full");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data->event = IOCPEVENT::QUIT;
|
||||||
|
spdlog::debug("Disconnected. [{}]", (std::string)(data->socket->remoteAddr));
|
||||||
|
::epoll_ctl(epollfd, EPOLL_CTL_DEL, data->socket->sock, NULL);
|
||||||
|
threadPool->enqueueJob(callback, data);
|
||||||
|
// delete data;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
totalSentSize += sentSize;
|
||||||
|
}
|
||||||
|
data->transferredbytes = totalSentSize;
|
||||||
|
threadPool->enqueueJob(callback, data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static void iocpWatcher(ThreadPool* threadPool, int epollfd, int epollDetroyerFd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
|
||||||
|
struct epoll_event events[FD_SETSIZE];
|
||||||
|
pthread_t tid = pthread_self();
|
||||||
|
|
||||||
|
int nready = ::epoll_wait(epollfd, events, FD_SETSIZE, -1);
|
||||||
|
|
||||||
|
for (int i=0; i<nready; i++) {
|
||||||
|
struct epoll_event current_event = events[i];
|
||||||
|
|
||||||
|
if (current_event.events & EPOLLIN) {
|
||||||
|
if (current_event.data.fd == epollDetroyerFd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::function<void(ThreadPool*, IOCPPASSINDATA*)> task(callback);
|
||||||
|
threadPool->enqueueJob(socketReader, current_event, epollfd, task);
|
||||||
|
}
|
||||||
|
else if (current_event.events & EPOLLOUT) {
|
||||||
|
std::function<void(ThreadPool*, IOCPPASSINDATA*)> task(callback);
|
||||||
|
threadPool->enqueueJob(socketWriter, current_event, epollfd, task);
|
||||||
|
}
|
||||||
|
if (--nready <= 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
threadPool->enqueueJob(iocpWatcher, epollfd, epollDetroyerFd, callback);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IOCP();
|
||||||
|
~IOCP();
|
||||||
|
|
||||||
|
template<typename _Callable>
|
||||||
|
void init(ThreadPool* __IOCPThread, _Callable&& callback) {
|
||||||
|
IOCPThread_ = __IOCPThread;
|
||||||
|
#ifdef _WIN32
|
||||||
|
completionPort_ = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
||||||
|
if (completionPort_ == NULL)
|
||||||
|
log::critical("CreateIoCompletionPort()");
|
||||||
|
#elif __linux__
|
||||||
|
epollfd_ = ::epoll_create(1);
|
||||||
|
epollDetroyerFd = ::eventfd(0, EFD_NONBLOCK);
|
||||||
|
struct epoll_event ev;
|
||||||
|
ev.events = EPOLLIN;
|
||||||
|
ev.data.fd = epollDetroyerFd;
|
||||||
|
::epoll_ctl(epollfd_, EPOLL_CTL_ADD, epollDetroyerFd, &ev);
|
||||||
|
#endif
|
||||||
|
auto boundFunc = [callback = std::move(callback)](ThreadPool* __IOCPThread, IOCPPASSINDATA* data) mutable {
|
||||||
|
callback(__IOCPThread, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int tCount = __IOCPThread->threadCount;
|
||||||
|
|
||||||
|
spdlog::info("Resizing threadpool size to: {}", tCount * 2);
|
||||||
|
|
||||||
|
__IOCPThread->respawnWorker(tCount * 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < tCount; i++) {
|
||||||
|
std::function<void(ThreadPool*, IOCPPASSINDATA*)> task(boundFunc);
|
||||||
|
__IOCPThread->enqueueJob(iocpWather, completionPort_, task);
|
||||||
|
}
|
||||||
|
#elif __linux__
|
||||||
|
__IOCPThread->respawnWorker(__IOCPThread->threadCount + 1);
|
||||||
|
spdlog::info("Spawning 1 Epoll Waiter...");
|
||||||
|
__IOCPThread->enqueueJob(iocpWatcher, epollfd_, epollDetroyerFd, boundFunc);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void destruct();
|
||||||
|
|
||||||
|
void registerSocket(IOCPPASSINDATA* data);
|
||||||
|
|
||||||
|
int recv(IOCPPASSINDATA* data, int bufferCount);
|
||||||
|
int send(IOCPPASSINDATA* data, int bufferCount, int __flags);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
int epollDetroyerFd = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct WSAManager wsaManager;
|
||||||
|
ThreadPool* IOCPThread_;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE completionPort_ = INVALID_HANDLE_VALUE;
|
||||||
|
#elif __linux__
|
||||||
|
int epollfd_ = -1;
|
||||||
|
std::unordered_map<std::shared_ptr<TCPSocket>, std::mutex> writeMutex;
|
||||||
|
std::unordered_map<std::shared_ptr<TCPSocket>, std::queue<IOCPPASSINDATA*>> writeBuffer;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "precomp.hpp"
|
|
||||||
|
|
||||||
namespace Chattr::log {
|
namespace Chattr::log {
|
||||||
void setDefaultLogger(spdlog::level::level_enum logLevel, gsl::czstring logFileName, std::uint32_t logFileSize, std::uint32_t logFileCount);
|
void setDefaultLogger(spdlog::level::level_enum logLevel, gsl::czstring logFileName, std::uint32_t logFileSize, std::uint32_t logFileCount);
|
||||||
|
|||||||
@@ -1,38 +1,43 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "precomp.hpp"
|
#include "Socket/Address.hpp"
|
||||||
#include "Address.hpp"
|
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
|
struct Address;
|
||||||
|
|
||||||
class Socket {
|
class Socket {
|
||||||
public:
|
public:
|
||||||
Socket();
|
Socket() = default;
|
||||||
Socket(int domain, int type, int protocol);
|
Socket(int domain, int type, int protocol);
|
||||||
~Socket();
|
~Socket();
|
||||||
|
|
||||||
void init(int domain, int type, int protocol);
|
int init(int domain, int type, int protocol);
|
||||||
|
void destruct();
|
||||||
|
|
||||||
operator SOCKET() const;
|
operator SOCKET();
|
||||||
void move(const SOCKET);
|
void set(const SOCKET __sock, int __domain);
|
||||||
|
int setsockopt(int level, int optname, const char* optval, int optlen);
|
||||||
|
|
||||||
void bind(sockaddr *__addr);
|
int bind(Address __addr);
|
||||||
void bind(sockaddr *__addr, socklen_t __len);
|
|
||||||
|
|
||||||
//IPV4
|
int recvfrom(void *__restrict __buf, size_t __n, int __flags, struct Address& __addr);
|
||||||
void bind(sockaddr_in *__addr);
|
int sendto(const void *__buf, size_t __n, int __flags, struct Address __addr);
|
||||||
void bind(sockaddr_in *__addr, socklen_t __len);
|
|
||||||
|
|
||||||
//IPV6
|
Socket(const Socket&);
|
||||||
void bind(sockaddr_in6 *__addr);
|
Socket(Socket&&) noexcept;
|
||||||
void bind(sockaddr_in6 *__addr, socklen_t __len);
|
Socket& operator=(const Socket&);
|
||||||
|
Socket& operator=(Socket&&) noexcept;
|
||||||
Socket(const Socket&) = delete;
|
|
||||||
Socket& operator=(const Socket&) = delete;
|
|
||||||
|
|
||||||
struct Address bindAddr = {};
|
struct Address bindAddr = {};
|
||||||
|
struct Address remoteAddr = {};
|
||||||
|
|
||||||
|
int domain = 0;
|
||||||
|
SOCKET sock = INVALID_SOCKET;
|
||||||
|
|
||||||
|
std::mutex readMutex;
|
||||||
|
std::mutex writeMutex;
|
||||||
protected:
|
protected:
|
||||||
bool valid_ = false;
|
bool valid_ = false;
|
||||||
SOCKET sock_ = INVALID_SOCKET;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Socket.hpp"
|
#include "Socket.hpp"
|
||||||
#include "precomp.hpp"
|
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
@@ -8,10 +7,12 @@ class TCPSocket : public Socket {
|
|||||||
public:
|
public:
|
||||||
using Socket::Socket;
|
using Socket::Socket;
|
||||||
using Socket::init;
|
using Socket::init;
|
||||||
void init(int domain);
|
int init(int domain);
|
||||||
void listen(int __n);
|
int listen(int __n);
|
||||||
void accept(TCPSocket& newSock, Address& addr);
|
void accept(TCPSocket& newSock, Address& addr);
|
||||||
void connect(Chattr::Address serveraddr);
|
int connect(Chattr::Address& serveraddr);
|
||||||
|
int recv(void *__restrict __buf, size_t __n, int __flags);
|
||||||
|
int send(const void *__buf, size_t __n, int __flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
10
include/Socket/WSAManager.hpp
Normal file
10
include/Socket/WSAManager.hpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
struct WSAManager {
|
||||||
|
WSAManager();
|
||||||
|
~WSAManager();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "precomp.hpp"
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
@@ -8,7 +7,8 @@ public:
|
|||||||
struct Config {
|
struct Config {
|
||||||
Json::Value configJsonRoot;
|
Json::Value configJsonRoot;
|
||||||
std::uint32_t ipVersion = 0;
|
std::uint32_t ipVersion = 0;
|
||||||
std::uint32_t listenPort = 0;
|
gsl::czstring IP = "";
|
||||||
|
std::uint32_t Port = 0;
|
||||||
spdlog::level::level_enum logLevel = spdlog::level::off;
|
spdlog::level::level_enum logLevel = spdlog::level::off;
|
||||||
gsl::czstring logFileName = "";
|
gsl::czstring logFileName = "";
|
||||||
std::uint32_t logfileSize = 0;
|
std::uint32_t logfileSize = 0;
|
||||||
32
include/Utils/Snowflake.hpp
Normal file
32
include/Utils/Snowflake.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
struct Snowflake {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
std::uint64_t timestamp : 42;
|
||||||
|
std::uint64_t instance : 10;
|
||||||
|
std::uint64_t sequence : 12;
|
||||||
|
};
|
||||||
|
std::uint64_t snowflake;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const Snowflake& other) const {
|
||||||
|
return snowflake == other.snowflake;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Snowflake GenerateID();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<Chattr::Snowflake> {
|
||||||
|
std::size_t operator()(const Chattr::Snowflake& k) const {
|
||||||
|
return std::hash<uint64_t>{}(k.snowflake);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
18
include/Utils/StringTokenizer.hpp
Normal file
18
include/Utils/StringTokenizer.hpp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
class StringTokenizer {
|
||||||
|
public:
|
||||||
|
StringTokenizer() {}
|
||||||
|
StringTokenizer(gsl::czstring string, std::uint32_t size);
|
||||||
|
|
||||||
|
void set(gsl::czstring string, std::uint32_t size);
|
||||||
|
std::vector<std::string> get();
|
||||||
|
|
||||||
|
operator std::vector<std::string>();
|
||||||
|
private:
|
||||||
|
std::queue<gsl::czstring> tokens;
|
||||||
|
std::queue<std::uint32_t> tokens_length;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -8,6 +8,10 @@
|
|||||||
#error "이 플랫폼은 지원되지 않습니다."
|
#error "이 플랫폼은 지원되지 않습니다."
|
||||||
#endif
|
#endif
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <future>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
#include "Socket/Log.hpp"
|
||||||
|
|
||||||
namespace Chattr {
|
namespace Chattr {
|
||||||
|
|
||||||
@@ -15,33 +19,75 @@ class Thread {
|
|||||||
public:
|
public:
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static unsigned __stdcall thread_func(LPVOID param) {
|
static unsigned __stdcall thread_func(LPVOID param) {
|
||||||
std::unique_ptr<std::function<void()>> func(static_cast<std::function<void()>*>(param));
|
auto task = static_cast<std::packaged_task<void()>*>(param);
|
||||||
(*func)();
|
(*task)();
|
||||||
|
delete task;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
static void* thread_func(void *param) {
|
static void* thread_func(void *param) {
|
||||||
std::unique_ptr<std::function<void()>> func(static_cast<std::function<void()>*>(param));
|
auto task = static_cast<std::packaged_task<void()>*>(param);
|
||||||
(*func)();
|
(*task)();
|
||||||
|
delete task;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
template<typename Callable, typename... Args>
|
Thread(Thread&&) noexcept;
|
||||||
Thread(Callable&& f, Args&&... args) {
|
Thread& operator=(Thread&&) noexcept;
|
||||||
auto boundFunc = std::bind(std::forward<Callable>(f), std::forward<Args>(args)...);
|
|
||||||
|
|
||||||
auto funcPtr = new std::function<void()>(boundFunc);
|
Thread(const Thread&) = delete;
|
||||||
|
Thread& operator=(Thread&) = delete;
|
||||||
|
|
||||||
|
template<typename _Callable, typename... _Args>
|
||||||
|
requires (!std::is_same_v<std::decay_t<_Callable>, Thread>) &&
|
||||||
|
(!std::is_void_v<std::invoke_result_t<_Callable, _Args...>>)
|
||||||
|
Thread(_Callable&& __f, _Args&&... __args) {
|
||||||
|
auto boundFunc = [this, __f, ... __args = std::move(__args)]() mutable {
|
||||||
|
returnValuePtr = std::make_shared<std::invoke_result_t<_Callable, _Args...>>(__f(std::move(__args)...));
|
||||||
|
};
|
||||||
|
std::packaged_task<void()>* funcPtr = new std::packaged_task<void()>(std::move(boundFunc));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
handle_ = (HANDLE)_beginthreadex(nullptr, 0, thread_func, funcPtr, 0, nullptr);
|
handle_ = (HANDLE)_beginthreadex(nullptr, 0, thread_func, funcPtr, 0, nullptr);
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
pthread_create(&handle_, NULL, thread_func, funcPtr);
|
pthread_create(&handle_, NULL, thread_func, funcPtr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template<typename _Callable, typename... _Args>
|
||||||
|
requires (!std::is_same_v<std::decay_t<_Callable>, Thread>) &&
|
||||||
|
std::is_void_v<std::invoke_result_t<_Callable, _Args...>>
|
||||||
|
Thread(_Callable&& __f, _Args&&... __args) {
|
||||||
|
auto boundFunc = [this, __f, ... __args = std::move(__args)]() mutable {
|
||||||
|
__f(std::move(__args)...);
|
||||||
|
};
|
||||||
|
std::packaged_task<void()>* funcPtr = new std::packaged_task<void()>(std::move(boundFunc));
|
||||||
|
#ifdef _WIN32
|
||||||
|
handle_ = (HANDLE)_beginthreadex(nullptr, 0, thread_func, funcPtr, 0, nullptr);
|
||||||
|
#elif __linux__
|
||||||
|
int rc = pthread_create(&handle_, NULL, thread_func, funcPtr);
|
||||||
|
if (handle_ <= 0 || rc != 0)
|
||||||
|
log::critical("pthread_create()");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
~Thread();
|
~Thread();
|
||||||
|
|
||||||
void join();
|
void join() {
|
||||||
void deatch();
|
#ifdef _WIN32
|
||||||
|
WaitForSingleObject(handle_, INFINITE);
|
||||||
|
#elif __linux__
|
||||||
|
pthread_join(handle_, nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template<typename _RetType>
|
||||||
|
requires (!std::is_void_v<_RetType>)
|
||||||
|
_RetType join() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WaitForSingleObject(handle_, INFINITE);
|
||||||
|
#elif __linux__
|
||||||
|
pthread_join(handle_, nullptr);
|
||||||
|
#endif
|
||||||
|
return *static_cast<_RetType *>(returnValuePtr);
|
||||||
|
}
|
||||||
|
void detach();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -49,6 +95,7 @@ private:
|
|||||||
#elif __linux__
|
#elif __linux__
|
||||||
pthread_t handle_;
|
pthread_t handle_;
|
||||||
#endif
|
#endif
|
||||||
|
std::shared_ptr<void> returnValuePtr;
|
||||||
bool detached = false;
|
bool detached = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
71
include/Utils/ThreadPool.hpp
Normal file
71
include/Utils/ThreadPool.hpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Thread.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <functional>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Chattr {
|
||||||
|
|
||||||
|
class ThreadPool {
|
||||||
|
public:
|
||||||
|
ThreadPool();
|
||||||
|
ThreadPool(std::uint32_t numThreads);
|
||||||
|
~ThreadPool();
|
||||||
|
|
||||||
|
void init(std::uint32_t numThreads);
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
void respawnWorker(std::uint32_t numThreads);
|
||||||
|
|
||||||
|
template<typename _Callable, typename... _Args>
|
||||||
|
requires (!std::is_void_v<std::invoke_result_t<_Callable, ThreadPool*, _Args...>>)
|
||||||
|
int enqueueJob(_Callable&& __job, std::invoke_result_t<_Callable, _Args...>& retVal, _Args&&... __args) {
|
||||||
|
if (terminate_) {
|
||||||
|
spdlog::error("Cannot run jobs on threads that terminating...");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(jobQueueMutex);
|
||||||
|
auto boundFunc = [this, &retVal, __job, ... __args = std::move(__args)]() mutable {
|
||||||
|
retVal = __job(this, std::move(__args)...);
|
||||||
|
};
|
||||||
|
auto task = std::packaged_task<void()>(std::move(boundFunc));
|
||||||
|
jobs_.push(std::move(task));
|
||||||
|
jobQueueCV_.notify_one();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
template<typename _Callable, typename... _Args>
|
||||||
|
requires std::is_void_v<std::invoke_result_t<_Callable, ThreadPool*, _Args...>>
|
||||||
|
int enqueueJob(_Callable&& __job, _Args&&... __args) {
|
||||||
|
if (terminate_) {
|
||||||
|
spdlog::error("Cannot run jobs on threads that terminating...");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(jobQueueMutex);
|
||||||
|
auto boundFunc = [this, __job, ... __args = std::move(__args)]() mutable {
|
||||||
|
__job(this, std::move(__args)...);
|
||||||
|
};
|
||||||
|
auto task = std::packaged_task<void()>(std::move(boundFunc));
|
||||||
|
jobs_.push(std::move(task));
|
||||||
|
jobQueueCV_.notify_one();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int threadCount = 0;
|
||||||
|
private:
|
||||||
|
void* Worker();
|
||||||
|
|
||||||
|
std::condition_variable jobQueueCV_;
|
||||||
|
std::mutex jobQueueMutex;
|
||||||
|
std::queue<std::packaged_task<void()>> jobs_;
|
||||||
|
std::vector<Chattr::Thread> workers_;
|
||||||
|
bool terminate_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,18 +4,25 @@
|
|||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <ws2bth.h>
|
#include <ws2bth.h>
|
||||||
|
#include <ws2def.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#define in_addr_t ULONG
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <sys/eventfd.h>
|
||||||
#define SOCKET int
|
#define SOCKET int
|
||||||
#define INVALID_SOCKET -1
|
#define INVALID_SOCKET -1
|
||||||
|
#define SOCKET_ERROR -1
|
||||||
#else
|
#else
|
||||||
#error "이 플랫폼은 지원되지 않습니다."
|
#error "이 플랫폼은 지원되지 않습니다."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <gsl/gsl>
|
#include <gsl/gsl>
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
Reference in New Issue
Block a user