Compare commits

...

26 Commits

Author SHA1 Message Date
8b486008fa 최종본 2025-05-10 19:44:45 +09:00
be6417cd40 서버측 로직 빼먹음 ㅋㅋ 2025-05-10 17:59:38 +09:00
7c382bead0 룸 내의 유저만 조회 기능 추가 2025-05-10 17:40:58 +09:00
91a3cfd9f4 overlapped 초기화 루틴 빼먹음 ㅋㅋㅋ 2025-05-10 11:52:46 +09:00
89306442ae IOCPPASSINDATA 복사 생성자,대입 연산자 추가 2025-05-10 11:32:15 +09:00
d5342b5f61 TCPSocket의 소유권 확인하기. 어디선가 예상치 못하게 socket이 close되었음 2025-05-10 02:25:28 +09:00
25527405d5 뮤텍스 범위(임계 영역) 재설정. 2025-05-10 02:04:11 +09:00
65b7aa8f6e 유저 연결이 끊어졌을 시에 방에서도 나가도록 수정 2025-05-10 01:33:29 +09:00
6c1b0c6852 진짜 그놈의 바이트수 병신같이 못맞추는건 왜 그러는거임? 2025-05-10 00:47:56 +09:00
7157e67040 아이디 중복방지 일단 서버측 구현? 2025-05-10 00:12:35 +09:00
be6ea6e92f 일단 클라이언트 코드 채움 2025-05-10 00:08:59 +09:00
20f8c7b29d 왠지 버그 고쳐짐.. 2025-05-09 23:31:25 +09:00
74e73ccc23 유저 삭제 루틴을 IOCP로 넘김 2025-05-09 23:00:27 +09:00
0aa312f67e future 2025-05-09 21:47:00 +09:00
78f2bfe2f6 bugfix 2025-05-09 20:02:38 +09:00
1ae7486cd5 덜 읽힌 데이터는 IOCPClient에서는 존재할 수 없음 2025-05-09 18:03:32 +09:00
df35dd43c1 bugfix 2025-05-09 17:53:03 +09:00
914cfcba32 서버 버그 픽스스 2025-05-09 17:19:53 +09:00
87cc1c7119 다중 접속시에 문제가 생김 2025-05-07 20:56:41 +09:00
46abb2b2b9 일단 동기화 2025-05-07 16:52:45 +09:00
993d41736d 과제 제출본.. 2025-05-04 23:43:04 +09:00
8ffe3a7519 snowflake id가 이상하게 생성되는 문제 해결결 2025-05-04 14:50:52 +09:00
e0a371d536 일단 이제 자잘한 버그만 고치면 될 듯? 2025-05-04 14:31:30 +09:00
63428ebf4d 도저히 여러 패킷 동시에 보내기를 할 수가 없다... 2025-05-04 03:26:02 +09:00
016d923d64 Merge branch 'main' of https://git.happytanuki.kr/HappyTanuki/NP_Midterm 2025-05-03 21:17:27 +09:00
0cd43f3d42 오타 잘 보고 기억하자... 2025-05-03 21:17:20 +09:00
19 changed files with 1302 additions and 259 deletions

View File

@@ -81,6 +81,11 @@
"expected": "cpp",
"complex": "cpp",
"__config": "cpp",
"ranges": "cpp"
"ranges": "cpp",
"valarray": "cpp",
"__tree": "cpp",
"map": "cpp",
"iostream": "cpp",
"any": "cpp"
}
}

View File

@@ -33,6 +33,7 @@ FetchContent_MakeAvailable(JSONCPP)
file(GLOB_RECURSE additional_sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
)
add_executable(${PROJECT_NAME}

View 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_;
};
}

View 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
View 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);
}
}

View 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;
}
}

View File

@@ -1,91 +1,14 @@
#define _CLIENT
#include "Socket/TCPSocket.hpp"
#include "Socket/Address.hpp"
#include "Socket/Log.hpp"
#include "Packet/Packet.hpp"
#include "Utils/Snowflake.hpp"
#include "ClientManager/ClientManager.hpp"
#include "precomp.hpp"
int main() {
Chattr::TCPSocket sock;
sock.init(AF_INET6);
Chattr::Address serveraddr(AF_INET6, "::1", 9011);
if (sock.connect(serveraddr) == INVALID_SOCKET) {
spdlog::error("{}", strerror(errno));
return 0;
}
spdlog::info("Connection established from {}", (std::string)serveraddr);
Chattr::Snowflake myId;
Chattr::LoginRequestPacket loginRequestPacket;
loginRequestPacket.__data.packetType = Chattr::PacketCategory::PACKET_REQUEST;
loginRequestPacket.__data.requestType = Chattr::RequestType::LOGIN;
loginRequestPacket.__data.dataType = Chattr::DataType::BINARY;
loginRequestPacket.__data.packetLength = 14;
memcpy(loginRequestPacket.__data.data, "Hello, World!", 14);
loginRequestPacket.convToN();
sock.send(loginRequestPacket.serialized, 8 + 14, 0);
Chattr::LoginResponsePacket loginResponsePacket;
sock.recv(loginResponsePacket.serialized, 18, 0);
loginResponsePacket.convToH();
::memcpy(&myId, loginResponsePacket.__data.yourId, sizeof(Chattr::Snowflake));
Chattr::UsersListRequestPacket usersListRequestPacket;
usersListRequestPacket.__data.packetType = Chattr::PacketCategory::PACKET_REQUEST;
usersListRequestPacket.__data.requestType = Chattr::RequestType::USERS_LIST;
usersListRequestPacket.__data.dataType = Chattr::DataType::TEXT;
usersListRequestPacket.__data.packetLength = 0;
usersListRequestPacket.convToN();
sock.send(usersListRequestPacket.serialized, 8, 0);
std::vector<std::pair<Chattr::Snowflake, std::string>> users;
Chattr::UsersListResponsePacket usersListResponsePacket;
sock.recv(usersListResponsePacket.serialized, 8, 0);
std::uint16_t dataLength = ::ntohs(usersListResponsePacket.__data.packetLength);
sock.recv(usersListResponsePacket.serialized + 8, dataLength, 0);
usersListRequestPacket.convToH();
int usersCount = usersListResponsePacket.__data.usersCount;
users.reserve(usersCount);
Chattr::Snowflake userId;
::memcpy(&userId.snowflake, usersListResponsePacket.__data.userId, sizeof(Chattr::Snowflake));
users.emplace_back(
userId,
std::string((char*)usersListResponsePacket.__data.name, usersListResponsePacket.__data.packetLength - 14)
);
for (int i = 0; i < usersCount - 1; i++) {
sock.recv(usersListResponsePacket.serialized, 8, 0);
std::uint16_t dataLength = ::ntohs(usersListResponsePacket.__data.packetLength);
sock.recv(usersListResponsePacket.serialized + 8, dataLength, 0);
usersListRequestPacket.convToH();
::memcpy(&userId.snowflake, usersListResponsePacket.__data.userId, sizeof(Chattr::Snowflake));
users.emplace_back(
userId,
std::string((char*)usersListResponsePacket.__data.name, usersListResponsePacket.__data.packetLength - 14)
);
}
Chattr::DataPostPacket dataPostPacket;
dataPostPacket.__data.packetType = Chattr::PacketCategory::PACKET_POST;
dataPostPacket.__data.requestType = Chattr::RequestType::DATA;
dataPostPacket.__data.dataType = Chattr::DataType::TEXT;
dataPostPacket.__data.packetLength = 14 + 8;
for (auto user : users)
if (user.first != myId) {
::memcpy(dataPostPacket.__data.destId, &user.first.snowflake, sizeof(Chattr::Snowflake));
break;
}
memcpy(dataPostPacket.__data.data, "Hello, World!", 14);
dataPostPacket.convToN();
sock.send(&dataPostPacket.serialized, 6 + 14 + 8, 0);
Chattr::ResponsePacket packet;
sock.recv(&packet.serialized, 10, 0);
packet.convToH();
Chattr::ClientManager client;
client.init();
client.run();
}

View File

@@ -22,21 +22,23 @@ public:
struct Address serveraddr;
if (config.ipVersion == 4) {
listenSock_.init(AF_INET);
serveraddr.set(AF_INET, INADDR_ANY, config.listenPort);
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, in6addr_any, config.listenPort);
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) {
_IOCPClient(thread, data);
this->_IOCPClient(thread, data);
});
}
@@ -46,14 +48,16 @@ public:
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);
void registerUser(std::string userName, std::shared_ptr<TCPSocket> sock);
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);
void createRoom(std::string roomName);
Snowflake createRoom(std::string roomName);
void deleteRoom(Snowflake RID);
std::vector<std::pair<Snowflake, std::string>> getRoomList();
@@ -80,7 +84,7 @@ private:
std::unordered_map<std::shared_ptr<TCPSocket>, Snowflake> userSocket2UID_;
std::unordered_map<Snowflake, std::string> userNames_;
std::unordered_map<std::string, Snowflake> userName2ID_;
};

View File

@@ -5,9 +5,24 @@
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;
@@ -15,8 +30,6 @@ void ServerManager::_IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDA
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) {
@@ -71,6 +84,13 @@ void ServerManager::_IOCPClient(Chattr::ThreadPool* thread, Chattr::IOCPPASSINDA
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);
@@ -135,6 +155,8 @@ PacketSet ServerManager::packetParser(Packet Packet) {
return PacketSet::ROOMEXITREQUEST;
case RequestType::USERS_LIST:
return PacketSet::USERSLISTREQUEST;
case RequestType::ROOM_USERS_LIST:
return PacketSet::ROOMUSERSLISTREQUEST;
case RequestType::DATA:
return PacketSet::INVALID;
}
@@ -153,6 +175,8 @@ PacketSet ServerManager::packetParser(Packet Packet) {
return PacketSet::ROOMEXITRESPONSE;
case RequestType::USERS_LIST:
return PacketSet::USERSLISTRESPONSE;
case RequestType::ROOM_USERS_LIST:
return PacketSet::ROOMUSERSLISTRESPONSE;
case RequestType::DATA:
return PacketSet::INVALID;
}
@@ -167,40 +191,141 @@ PacketSet ServerManager::packetParser(Packet Packet) {
}
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);
registerUser(userName, data->socket);
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 loginResponsePacket;
loginResponsePacket.__data.packetType = Chattr::PacketCategory::PACKET_RESPONSE;
loginResponsePacket.__data.requestType = Chattr::RequestType::LOGIN;
loginResponsePacket.__data.dataType = Chattr::DataType::TEXT;
loginResponsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode) + sizeof(Snowflake);
loginResponsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::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, 16);
data->sendbytes = 16;
data->wsabuf.len = 16;
data->IOCPInstance->send(data, 1, 0);
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) {
@@ -209,20 +334,15 @@ void ServerManager::processUsersListRequestPacket(UsersListRequestPacket usersLi
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::OK;
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;
::memset(&ptr->overlapped, 0, sizeof(OVERLAPPED));
ptr->socket = data->socket;
ptr->recvbytes = ptr->sendbytes = 0;
ptr->wsabuf.buf = ptr->buf;
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
ptr->wsabuf.len = packetLength + 8;
ptr->IOCPInstance = data->IOCPInstance;
usersListResponsePacket.convToN();
memcpy(ptr->wsabuf.buf, usersListResponsePacket.serialized, packetLength + 8);
@@ -231,7 +351,38 @@ void ServerManager::processUsersListRequestPacket(UsersListRequestPacket usersLi
}
}
void ServerManager::processDataPostPacket(DataPostPacket dataPostPacket, IOCPPASSINDATA* data) {
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));
@@ -253,7 +404,7 @@ void ServerManager::processDataPostPacket(DataPostPacket dataPostPacket, IOCPPAS
responsePacket.__data.requestType = Chattr::RequestType::DATA;
responsePacket.__data.dataType = Chattr::DataType::TEXT;
responsePacket.__data.packetLength = sizeof(Chattr::ResponseStatusCode);
responsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::OK;
responsePacket.__data.responseStatusCode = Chattr::ResponseStatusCode::RES_OK;
responsePacket.convToN();
memcpy(data->wsabuf.buf, responsePacket.serialized, 10);
@@ -263,36 +414,76 @@ void ServerManager::processDataPostPacket(DataPostPacket dataPostPacket, IOCPPAS
int packetLength = dataPostPacket.__data.packetLength;
dataPostPacket.convToN();
for (auto dest : destinationSockets) {
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA;
::memset(&ptr->overlapped, 0, sizeof(OVERLAPPED));
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA(*data);
ptr->socket = dest;
ptr->recvbytes = ptr->sendbytes = 0;
ptr->wsabuf.buf = ptr->buf;
ptr->wsabuf.len = packetLength + 6;
ptr->IOCPInstance = data->IOCPInstance;
ptr->wsabuf.len = packetLength + 8;
dataPostPacket.convToN();
memcpy(ptr->wsabuf.buf, dataPostPacket.serialized, packetLength + 6);
data->sendbytes = packetLength + 6;
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);
}
}
void ServerManager::registerUser(std::string userName, std::shared_ptr<TCPSocket> sock) {
std::lock_guard<std::mutex> lock(resourceMutex_);
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) {
std::lock_guard<std::mutex> lock(resourceMutex_);
userNames_.erase(UID);
std::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
UID2userSocket_.erase(UID);
@@ -300,7 +491,6 @@ void ServerManager::deleteUser(Snowflake UID) {
}
std::vector<std::pair<Snowflake, std::string>> ServerManager::getUserList() {
std::lock_guard<std::mutex> lock(resourceMutex_);
std::vector<std::pair<Snowflake, std::string>> userList;
userList.reserve(userNames_.size());
@@ -310,21 +500,30 @@ std::vector<std::pair<Snowflake, std::string>> ServerManager::getUserList() {
return userList;
}
void ServerManager::createRoom(std::string roomName) {
std::lock_guard<std::mutex> lock(resourceMutex_);
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) {
std::lock_guard<std::mutex> lock(resourceMutex_);
roomNames_.erase(RID);
rooms_.erase(RID);
}
std::vector<std::pair<Snowflake, std::string>> ServerManager::getRoomList() {
std::lock_guard<std::mutex> lock(resourceMutex_);
std::vector<std::pair<Snowflake, std::string>> roomList;
roomList.reserve(roomNames_.size());
@@ -335,13 +534,11 @@ std::vector<std::pair<Snowflake, std::string>> ServerManager::getRoomList() {
}
void ServerManager::joinRoom(Snowflake UID, Snowflake RID) {
std::lock_guard<std::mutex> lock(resourceMutex_);
std::shared_ptr<TCPSocket> sock = UID2userSocket_[UID];
rooms_[RID][UID] = sock;
}
void ServerManager::exitRoom(Snowflake UID, Snowflake RID) {
std::lock_guard<std::mutex> lock(resourceMutex_);
rooms_[RID].erase(UID);
}
@@ -349,13 +546,9 @@ void ServerManager::run() {
while (true) {
spdlog::info("Waiting for connection...");
listenSock_.accept(clientSock_, clientAddr_);
bool enable = true;
clientSock_.setsockopt(SOL_SOCKET, SO_KEEPALIVE, (const char*)&enable, sizeof(enable));
spdlog::trace("Accepted connection from {}, [{}]", clientSock_.sock, (std::string)clientAddr_);
Chattr::IOCPPASSINDATA* ptr = new Chattr::IOCPPASSINDATA;
::memset(&ptr->overlapped, 0, sizeof(OVERLAPPED));
ptr->socket = std::make_shared<TCPSocket>(std::move(clientSock_));
ptr->recvbytes = ptr->sendbytes = 0;
ptr->wsabuf.buf = ptr->buf;
ptr->wsabuf.len = 1500;
ptr->IOCPInstance = &iocp_;

25
Server/src/Socket/Log.cpp Normal file
View 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);
}
}

View File

@@ -13,7 +13,8 @@ ConfigManager::Config ConfigManager::load() {
if (!configfile.is_open()) {
std::ofstream defaultConfig("config.json", std::ios::out);
config.configJsonRoot["IP Version"] = 6;
config.configJsonRoot["Listen Port"] = 9010;
config.configJsonRoot["IP"] = "::";
config.configJsonRoot["Port"] = 9010;
config.configJsonRoot["LogLevel"] = 1;
config.configJsonRoot["LogfileName"] = "Chattering.log";
config.configJsonRoot["LogfileSize"] = UINT32_MAX;
@@ -30,8 +31,10 @@ ConfigManager::Config ConfigManager::load() {
if (config.ipVersion != 4 && config.ipVersion != 6)
throw std::runtime_error("Invalid IP Version.");
config.listenPort = config.configJsonRoot["Listen Port"].asInt();
if (config.listenPort < 0 || config.listenPort > 65535)
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();

View File

@@ -5,7 +5,24 @@
namespace Chattr {
void IOCP::registerSocket(Chattr::IOCPPASSINDATA* data) {
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)
@@ -17,6 +34,7 @@ void IOCP::registerSocket(Chattr::IOCPPASSINDATA* data) {
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)
@@ -37,15 +55,16 @@ int IOCP::recv(Chattr::IOCPPASSINDATA* data, int bufferCount) {
#endif
}
int IOCP::send(Chattr::IOCPPASSINDATA* data, int bufferCount, int __flags) {
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 = EPOLLOUT | EPOLLONESHOT;
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
}

View File

@@ -10,18 +10,6 @@
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) {
#ifdef _WIN32
LPSTR msgbuf = nullptr;

View File

@@ -32,8 +32,11 @@ int TCPSocket::connect(Address& serveraddr) {
int TCPSocket::recv(void *__restrict __buf, size_t __n, int __flags) {
int retVal = ::recv(sock, (char *)__buf, __n, __flags);
if (retVal == SOCKET_ERROR)
if (retVal == SOCKET_ERROR) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return retVal;
log::error("recv()");
}
return retVal;
}

View File

@@ -3,25 +3,28 @@
namespace Chattr {
Snowflake GenerateID() {
static struct _EPOCH {
_EPOCH() {
static struct EpochInitializer {
EpochInitializer() {
EPOCH = std::chrono::system_clock::now();
}
std::chrono::system_clock::time_point EPOCH;
} __EPOCH__;
} epochInitializer;
static std::mutex snowflakeGenerateMutex_;
std::lock_guard<std::mutex> lock(snowflakeGenerateMutex_);
#ifdef _WIN32
DWORD tid = GetCurrentThreadId();
#elif __linux__
pthread_t tid = pthread_self();
#endif
static int sequence = 0;
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() - __EPOCH__.EPOCH);
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++;

View File

@@ -12,6 +12,7 @@ enum class PacketSet {
ROOMJOINREQUEST,
ROOMEXITREQUEST,
USERSLISTREQUEST,
ROOMUSERSLISTREQUEST,
DATAPOST,
CONTINUE,
RESPONSE,
@@ -21,6 +22,7 @@ enum class PacketSet {
ROOMJOINRESPONSE,
ROOMEXITRESPONSE,
USERSLISTRESPONSE,
ROOMUSERSLISTRESPONSE,
INVALID
};
@@ -38,6 +40,7 @@ enum class RequestType : std::uint8_t {
ROOM_JOIN,
ROOM_EXIT,
USERS_LIST,
ROOM_USERS_LIST,
DATA
};
@@ -84,29 +87,93 @@ public:
RequestType requestType;
DataType dataType;
std::uint16_t packetLength;
std::uint16_t destId[4];
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.destId[i] = ::htons(__data.destId[i]);
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.destId[i] = ::ntohs(__data.destId[i]);
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 {};
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 {
@@ -115,6 +182,7 @@ public:
RequestType requestType;
DataType dataType;
std::uint16_t packetLength;
std::uint16_t sourceId[4];
std::uint16_t destId[4];
std::uint8_t data[];
} __data;
@@ -122,14 +190,18 @@ public:
};
std::uint8_t* convToN() {
__data.packetLength = ::htons(__data.packetLength);
for (int i = 0; i < 4; i++)
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++)
for (int i = 0; i < 4; i++) {
__data.sourceId[i] = ::ntohs(__data.sourceId[i]);
__data.destId[i] = ::ntohs(__data.destId[i]);
}
return serialized;
}
};
@@ -162,7 +234,7 @@ public:
};
enum class ResponseStatusCode : std::uint16_t {
OK = 200,
RES_OK = 200,
CREATED = 201,
ACCEPTED = 202,
BAD_REQUEST = 400,

View File

@@ -6,10 +6,11 @@
#include "Packet/Packet.hpp"
#include <functional>
#include <vector>
#include <queue>
#include "precomp.hpp"
#ifndef _WIN32
#ifdef __linux__
typedef struct _OVERLAPPED {
char dummy;
@@ -27,7 +28,7 @@ namespace Chattr {
class IOCP;
enum class IOCPEVENT {
ERR,
QUIT,
READ,
WRITE
};
@@ -42,6 +43,60 @@ struct IOCPPASSINDATA {
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 {
@@ -55,9 +110,11 @@ public:
DWORD cbTransfrred;
int retVal = GetQueuedCompletionStatus(completionPort_, &cbTransfrred, (PULONG_PTR)&sock, (LPOVERLAPPED*)&data, INFINITE);
if (retVal == 0 || cbTransfrred == 0) {
spdlog::debug("Client disconnected. [{}]", (std::string)(data->socket->remoteAddr));
delete data;
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;
@@ -66,117 +123,166 @@ public:
};
#elif __linux__
static void socketReader(ThreadPool* threadPool, epoll_event event, int epollfd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
IOCPPASSINDATA* iocpData = (IOCPPASSINDATA*)event.data.ptr;
pthread_t tid = pthread_self();
if (iocpData == nullptr) {
if (event.data.ptr == nullptr) {
spdlog::error("invalid call on {}", tid);
return;
}
IOCPPASSINDATA* rootIocpData = (IOCPPASSINDATA*)event.data.ptr;
std::lock_guard<std::mutex> lock(iocpData->socket->readMutex);
spdlog::trace("reading on tid: {} [{}]", tid, (std::string)iocpData->socket->remoteAddr);
int redSize = 0;
int headerSize = 8;
int totalRedSize = 0;
while (totalRedSize < headerSize) {
redSize = iocpData->socket->recv(iocpData->buf, headerSize - totalRedSize, 0);
if (redSize <= 0) {
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("{}", strerror(errno));
break;
} else {
spdlog::debug("Client disconnected. [{}]", (std::string)iocpData->socket->remoteAddr);
::epoll_ctl(epollfd, EPOLL_CTL_DEL, iocpData->socket->sock, NULL);
delete iocpData;
spdlog::trace("No data to read on {}", tid);
return;
}
}
totalRedSize += redSize;
}
Packet packet;
::memcpy(&packet.serialized, iocpData->buf, headerSize);
redSize = 0;
int dataLength = ntohs(packet.__data.packetLength);
while (totalRedSize < dataLength + headerSize) {
redSize = iocpData->socket->recv(iocpData->buf + totalRedSize, dataLength + headerSize - totalRedSize, 0);
if (redSize <= 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
spdlog::trace("{}", strerror(errno));
break;
} else {
spdlog::debug("Client disconnected. [{}]", (std::string)iocpData->socket->remoteAddr);
::epoll_ctl(epollfd, EPOLL_CTL_DEL, iocpData->socket->sock, NULL);
delete iocpData;
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;
}
}
totalRedSize += redSize;
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);
}
iocpData->transferredbytes = totalRedSize;
threadPool->enqueueJob(callback, iocpData);
};
static void socketWriter(ThreadPool* threadPool, epoll_event event, int epollfd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
IOCPPASSINDATA* iocpData = (IOCPPASSINDATA*)event.data.ptr;
pthread_t tid = pthread_self();
if (iocpData == nullptr) {
if (event.data.ptr == nullptr) {
spdlog::error("invalid call on {}", tid);
return;
}
IOCPPASSINDATA* rootIocpData = (IOCPPASSINDATA*)event.data.ptr;
std::lock_guard<std::mutex> lock(iocpData->socket->writeMutex);
std::lock_guard<std::mutex> lock(rootIocpData->socket->writeMutex);
while (!rootIocpData->sendQueue->empty()) {
IOCPPASSINDATA* data = rootIocpData->sendQueue->front();
rootIocpData->sendQueue->pop();
spdlog::trace("Writing on tid: {} [{}]", tid, (std::string)iocpData->socket->remoteAddr);
if (data == nullptr) {
spdlog::error("invalid call on {}", tid);
break;
}
int packetSize = iocpData->transferredbytes;
int totalSentSize = 0;
int sentSize = 0;
int packetSize = data->wsabuf.len;
int totalSentSize = 0;
int sentSize = 0;
spdlog::trace("Sending to: [{}]", (std::string)iocpData->socket->remoteAddr);
spdlog::trace("Sending to: [{}]", (std::string)data->socket->remoteAddr);
while (totalSentSize < packetSize) {
sentSize = iocpData->socket->send(iocpData->buf, packetSize - totalSentSize, 0);
while (totalSentSize < packetSize) {
sentSize = data->socket->send(data->buf + totalSentSize, packetSize - totalSentSize, 0);
if (sentSize <= 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
spdlog::trace("{}", strerror(errno));
break;
} else {
spdlog::debug("Client disconnected. [{}]", (std::string)iocpData->socket->remoteAddr);
::epoll_ctl(epollfd, EPOLL_CTL_DEL, iocpData->socket->sock, NULL);
delete iocpData;
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;
}
totalSentSize += sentSize;
data->transferredbytes = totalSentSize;
threadPool->enqueueJob(callback, data);
}
iocpData->transferredbytes = totalSentSize;
threadPool->enqueueJob(callback, iocpData);
};
static void iocpWatcher(ThreadPool* threadPool, int epollfd, std::function<void(ThreadPool*, IOCPPASSINDATA*)> callback) {
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();
spdlog::trace("epoll waiting on {}", tid);
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);
}
@@ -184,11 +290,16 @@ public:
std::function<void(ThreadPool*, IOCPPASSINDATA*)> task(callback);
threadPool->enqueueJob(socketWriter, current_event, epollfd, task);
}
if (--nready <= 0)
break;
}
threadPool->enqueueJob(iocpWatcher, epollfd, callback);
threadPool->enqueueJob(iocpWatcher, epollfd, epollDetroyerFd, callback);
};
#endif
IOCP();
~IOCP();
template<typename _Callable>
void init(ThreadPool* __IOCPThread, _Callable&& callback) {
IOCPThread_ = __IOCPThread;
@@ -198,6 +309,11 @@ public:
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);
@@ -215,24 +331,33 @@ public:
__IOCPThread->enqueueJob(iocpWather, completionPort_, task);
}
#elif __linux__
__IOCPThread->respawnWorker(__IOCPThread->threadCount + 1);
spdlog::info("Spawning 1 Epoll Waiter...");
__IOCPThread->enqueueJob(iocpWatcher, epollfd_, boundFunc);
__IOCPThread->enqueueJob(iocpWatcher, epollfd_, epollDetroyerFd, boundFunc);
#endif
}
void registerSocket(Chattr::IOCPPASSINDATA* data);
void destruct();
int recv(Chattr::IOCPPASSINDATA* data, int bufferCount);
int send(Chattr::IOCPPASSINDATA* data, int bufferCount, int __flags);
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 Chattr::WSAManager wsaManager;
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
};

View File

@@ -7,7 +7,8 @@ public:
struct Config {
Json::Value configJsonRoot;
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;
gsl::czstring logFileName = "";
std::uint32_t logfileSize = 0;

View File

@@ -15,6 +15,7 @@
#include <fcntl.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/eventfd.h>
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1