IRC_SERVER
By @hyunjunk (hyunjun2372@gmail.com)
Loading...
Searching...
No Matches
Server.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstring>
4#include <string>
5#include <vector>
6#include <unistd.h>
7#include <fcntl.h>
8#include <map>
9
10#include <sys/socket.h>
11#include <sys/types.h>
12#include <sys/event.h>
13#include <sys/time.h>
14#include <arpa/inet.h>
15
19#include "Server/MsgBlock.hpp"
21#include "Server/IrcReplies.hpp"
24
25#include "Core/Core.hpp"
26using namespace IRCCore;
27
28
29namespace IRC
30{
31
32 /** @class Server
33 * @nosubgrouping
34 *
35 * @internal
36 * @note See [ \ref irc_server_event_loop_process_flow ] before reading implementation details.
37 *
38 *
39 * @page irc_server_event_loop_process_flow Server Event Loop Process Flow
40 * @tableofcontents
41 *
42 * ## Kqueue & Kevent
43 * 메인 이벤트 루프는 kqueue를 사용하여 이벤트를 처리합니다.
44 * 모든 클라이언트는 mhKqueue에 등록되며 소켓이 닫히게되면 kqueue에서 자동으로 제거됩니다.
45 *
46 * ### 이벤트 등록
47 * Kqueue에 이벤트 등록을 하기 위해선 kevent() 함수를 호출하여야 하지만 이는 system call이므로 최대한 줄이는 것이 좋습니다.
48 * 그러므로 일반적인 Kevent 등록은 mEventRegistrationQueue에 추가하고 다음 이벤트 루프 시작점에서 한 번에 처리합니다.
49 *
50 * ## 메시지
51 * 메시지는 기본적으로 MsgBlock 클래스로 저장됩니다.
52 *
53 * ### 메시지 수신
54 * Recv()로 메시지를 받아 해당하는 클라이언트의 ClientControlBlock::RecvMsgBlocks 에 추가합니다.
55 * 클라이언트가 가진 마지막 메시지 블록에 남은 공간이 있다면 해당 공간에, 없다면 새로운 메시지 블록 공간에 가능한 byte만큼 recv합니다.
56 * 수신받은 메시지는 TCP 속도저하를 방지하기 위해 대기열에 추가되며, 즉시 처리하지 않습니다.
57 * 메시지 처리 대기열은 이벤트가 발생하지 않는 여유러운 시점에 처리됩니다.
58 * 그러므로 해당하는 클라이언트에 처리할 메시지가 있다는 것을 나타내기 위해 해당 클라이언트를 receivedClientMsgProcessQueue 목록에 추가해야합니다.
59 *
60 * ### 메시지 전송
61 * 서버에서 클라이언트로 메시지를 보내는 경우, 송신될 메시지는 각 클라이언트의 ClientControlBlock::MsgSendingQueue 에 추가되며 kqueue를 통해 비동기적으로 처리됩니다.
62 * 기본적으로 클라이언트 소켓에 대한 kevent는 WRITE 이벤트에 대한 필터가 비활성화 됩니다.
63 * 전송할 메시지가 생긴 경우, 해당 클라이언트 소켓에 대한 kevent에 WRITE 이벤트 필터를 활성화합니다.
64 * kqueue 이벤트 루프에 의해 모든 전송이 끝난 후 WRITE 이벤트 필터는 다시 비활성화됩니다.
65 *
66 * 또한 동일한 메시지를 여러 클라이언트에게 보내는 경우, 하나의 메시지 블록을 SharedPtr로 공유하여 사용 가능합니다.
67 *
68 * @see MessageSending section in IRC::Server class
69 *
70 * ### 메시지 처리 및 실행
71 * 각 클라이언트의 RecvMsgBlocks 에 저장된 수신 메시지들은 "\r\n"을 기준으로 분리되어 있지 않습니다.
72 * 그러므로 separateMsgsFromClientRecvMsgs() 함수를 통해 수신 메시지를 "\r\n"을 기준으로 분리한 뒤,
73 * processClientMsg() 함수에서 단일 메시지의 커맨드를 식별 후 해당하는 커맨드 실행 함수를 호출합니다.
74 * 각 커맨드 실행 함수는 클라이언트의 권한 및 유효성 검사, 실행, 모든 응답을 처리합니다.
75 *
76 * 각 커맨드 실행 함수는 executeClientCommand_<COMMAND>() 형태로 정의되어 있습니다.
77 * @see ClientCommandExecution section in IRC::Server class
78 *
79 * ## 클라이언트
80 * ### 클라이언트 생성
81 * 클라이언트가 최초로 Accept()되면 클라이언트의 ClientControlBlock이 생성되고 클라이언트 소켓에 대한 kevent가 등록됩니다.
82 * 해당 ClientControlBlock은 mUnregistedClients 목록에 추가되고, 후에 PASS/NICK/USER 명령어가 통과되면 mClients 목록으로 이동됩니다.
83 *
84 * ### 클라이언트 소멸
85 * 클라이언트 생성 이후 소켓에 오류가 발생하거나, 클라이언트가 QUIT 명령어를 보내거나 하면 해당 클라이언트는 소멸됩니다.
86 * 클라이언트는 2가지 종류의 종료가 있습니다.
87 * - forceDisconnectClient()
88 * 곧바로 클라이언트 소켓을 닫습니다.
89 * - disconnectClient()
90 * 클라이언트에게 disconnect 메시지를 보내고 소켓을 닫아야하는 경우에 사용합니다.
91 * 이 경우 클라이언트는 bExpired 플래그를 설정하고, 남은 메시지 전송이 끝난 후 소켓을 닫습니다.
92 *
93 * 소켓이 닫힌 클라이언트의 리소스는 후속 이벤트 루프에서 접근할 가능성이 있으므로, 클라이언트는 mClientReleaseQueue 목록에 추가되어 이벤트 루프에서 소멸됩니다.
94 *
95 * ## 리소스 해제
96 * 소켓을 제외한 대부분의 리소스는 SharedPtr를 사용하여 관리되기 때문에 명시적인 해제가 필요하지 않습니다.
97 * 채널 또한 SharedPtr에 의하여 모든 클라이언트가 나가게 되면 자동으로 해제됩니다.
98 *
99 **/
100 class Server
101 {
102 public:
103 /** Create a server instance.
104 *
105 * @param outPtrServer [out] Pointer to receive an instance.
106 * @param port Port number to listen.
107 * @param password Password to access server.
108 * see Constants::SVR_PASS_MIN, Constants::SVR_PASS_MAX
109 */
110 static EIrcErrorCode CreateServer(Server** outPtrServer, const std::string& serverName, const unsigned short port, const std::string& password);
111
112 /** Initialize resources and start the server event loop.
113 *
114 * @details Initialize kqueue and resources and register listen socket to kqueue.
115 * And then call eventLoop() to start the server.
116 *
117 * @note \li Blocking until the server is terminated.
118 * \li All non-static methods must be called after this function is called.
119 */
121
122 ~Server();
123
124 private:
125 Server(const std::string& serverName, const unsigned short port, const std::string& password);
126
127 /** @warning Copy constructor is not allowed. */
128 UNUSED Server& operator=(const Server& rhs);
129
130 /** A main event loop of the server. */
132
133 /** Destroy all resources of the server.
134 *
135 * @note After calling this function, the server instance is no longer available.
136 */
138
139 private:
140 /** @name Message Processing */
141 ///@{
142 /** Separate all separable messages in the client's ClientControlBlock::RecvMsgBlocks
143 *
144 * @param client A client to separate messages.
145 * @param outSeparatedMsgs [out] Vector to receive the separated messages without CR-LF.
146 * If the vector is not empty, the separated messages are appended to the end of the vector.
147 *
148 * @see ClientControlBlock::RecvMsgBlockCursor
149 */
151
152 /** Execute and reply the client's single message.
153 *
154 * @param client The client to process the message.
155 * @param msg The single message to process. It contains CR-LF.
156 * And it will be modified during the processing.
157 *
158 * @see ReplyMsgMakingFunctions
159 */
161 ///@}
162
163 /** Get SharedPtr to the ClientControlBlock from the kevent's udata. */
165 {
166 // @see SharedPtr::GetControlBlock(), mhKqueue
167 return SharedPtr<ClientControlBlock>(reinterpret_cast< detail::ControlBlock< ClientControlBlock >* >(event.udata));
168 }
169
170 private:
171 /** Client command execution function type
172 * @see ClientCommandExecution section in IRC::Server class
173 */
174 typedef IRC::EIrcErrorCode (Server::*ClientCommandFuncPtr)(SharedPtr<ClientControlBlock> client, const std::vector<char*>& arguments);
175
176 /**
177 * @name Client command execution
178 * @anchor ClientCommandExecution
179 * @brief Execute and reply the client command.
180 *
181 * Each function handles permission and validity checks, execution, and all replies.
182 */
183 ///@{
184#define IRC_CLIENT_COMMAND_X(command_name) IRC::EIrcErrorCode executeClientCommand_##command_name(SharedPtr<ClientControlBlock> client, const std::vector<char*>& arguments);
185 /**
186 * @param client [in] The client to process the command.
187 * @param arguments [in] Unvalidated arguments that separated by space.
188 * Example: "JOIN #channel1,#channel2 key1 key2 :h ello!@:world"
189 * arguments[0] = "#channel1,#channel2"
190 * arguments[1] = "key1"
191 * arguments[2] = "key2"
192 * arguments[3] = "h ello!@:world"
193 *
194 * @return The error code of the command execution.
195 * @see Server/ClientCommand/<COMMAND>.cpp
196 */
198#undef IRC_CLIENT_COMMAND_X
199 ///@}
200
201 private:
202 /**
203 * @name Client disconnection
204 * @note The releases of the disconnected clients are deferred to the next main event loop for the remaining kevents of the client.
205 */
206 ///@{
207 /** Close the client socket and release the client. */
208 EIrcErrorCode forceDisconnectClient(SharedPtr<ClientControlBlock> client, const std::string quitMessage = "");
209
210 /** Mark the client's bExpired flag and block the messages from the client, then close the socket after remaining messages are sent.
211 *
212 * Sending to the client is not able after calling this function.
213 */
214 EIrcErrorCode disconnectClient(SharedPtr<ClientControlBlock> client, const std::string quitMessage = "");
215 ///@}
216
217 /** Register a client to the server.
218 *
219 * Reply the result of the registration.
220 *
221 * @param client The client to register.
222 * @return Result of the registration.
223 */
225
226 /** Join a client to the exist channel without any error/permission check. */
228
229 /** Part a client from the channel without any error/permission check. */
231
232 SharedPtr<ClientControlBlock> findClientGlobal(const std::string& nickname);
233
234 SharedPtr<ChannelControlBlock> findChannelGlobal(const std::string& channelName);
235
236 /**
237 * @name Message sending
238 * @note \li Do not modify the passed message after calling this function.
239 * \li No check permission of the client to send the message.
240 */
241 ///@{
242 /** Send a message to client.
243 *
244 * @param client The client to send the message.
245 * @param msg The message to send. It can contain CR-LF or not.
246 */
248
249 /** Send a message to channel members.
250 *
251 * @param channel The channel to send the message.
252 * @param msg The message to send. It can contain CR-LF or not.
253 * @param client A client to exclude from the message sending.
254 */
256
257 /** Send a message to channels the client is connected
258 *
259 * @param client The client to send the message.
260 * This client is excluded from the message sending.
261 * @param msg The message to send. It can contain CR-LF or not.
262 */
264 ///@}
265
266 private:
267 /** @name Logging */
268 ///@{
269 void logErrorCode(EIrcErrorCode errorCode) const;
270
271 void logMessage(const std::string& message) const;
272
273 void logVerbose(const std::string& message) const;
274 ///@}
275
276 private:
277 std::string mServerName;
279 std::string mServerPassword;
280
282
283 /**
284 * @name Kqueue
285 * @brief Data in the udata of kevent is the controlBlock of the SharedPtr to corresponding ClientControlBlock (except listensocket).
286 *
287 * @see \li SharedPtr::GetControlBlock(), getClientFromKeventUdata()
288 * \li [ \ref irc_server_kqueue_udata ]
289 *
290 * @page irc_server_kqueue_udata Technical reason for using the controlBlock of SharedPtr in the kqueue udata
291 * ## Details
292 * kevent의 udata에 controlBlock을 넣는 기술적 이유는 다음과 같습니다.
293 *
294 * kevent의 이벤트가 반환되었을 때, ident 필드의 file descriptor를 통해 클라이언트 인스턴스를 찾아야 하는데
295 * 이 탐색비용을 줄이기 위해 예약된 udata에 클라이언트의 인스턴스 주소를 넣어두고 곧바로 접근 가능하도록 합니다.
296 *
297 * 이를 위한 원래 계획은 ClientControlBlock의 raw pointer를 사용하는 것이었으나,
298 * ClientControlBlock에 의존하는 리소스의 수가 증가함에 따라
299 * 메모리를 해제하는 시점이 복잡해지는 문제가 발생하여 Shared Pointer를 사용하기로 결정했습니다.
300 *
301 * 하지만 Shared Pointer는 void pointer로 변환할 수 없기 때문에 직접 사용할 수 없었고,
302 * 어쩔 수 없이 Shared Pointer의 controlBlock을 udata로 사용하자는 복잡한 구현을 선택을 했습니다.
303 *
304 * Shared pointer를 사용하지 않는 것 보다는 이렇게 라도 사용하는 것이
305 * 전체 프로젝트에서 메모리 해제에 대한 걱정을 없애주고,
306 * 단지 udata와 직접적으로 상호작용하는 작은 코어 부분만 복잡하게 만들기에 유리하다고 판단되었습니다.
307 *
308 * @see Server::mhKqueue, SharedPtr::GetControlBlock(), getClientFromKeventUdata()
309 */
310 ///@{
312 std::vector<kevent_t> mEventRegistrationQueue;
313 ///@}
314
315 /**
316 * @name Client lists
317 * @warning Do not release a client if the client's event is still exists in the kqueue.
318 */
319 ///@{
320 std::vector< SharedPtr< ClientControlBlock > > mUnregistedClients;
321
322 /** Nickname to client map */
323 std::map< std::string, SharedPtr< ClientControlBlock > > mClients;
324 ///@}
325
326 /** Queue to release expired clients
327 *
328 * @see ClientDisconnection section in IRC::Server class
329 */
330 std::vector< SharedPtr< ClientControlBlock > > mClientReleaseQueue;
331
332 /** Channel name to channel map
333 *
334 * @warning
335 * # Caution for using WeakPtr
336 * ## [한국어]
337 * WeakPtr이 Expired 되어도 map에서 자동으로 제거되지 않습니다.
338 * map을 직접 조작하는 경우 다음과 같은 문제를 주의해야 합니다.
339 * 만약 expired 된 엔트리가 제거되지 않은 상태에서 동일한 key로 map::insert() 를 시도하면 덮어씌워지지 않습니다. (map::insert()는 동일한 key가 있을 경우 실패합니다.)
340 * 이러한 경우를 방지하기 위해 expired 된 엔트리를 수동으로 제거하거나, map::insert() 대신 map::operator[] 를 사용해야합니다. (operator[]는 동일한 key가 있을 경우 덮어씌웁니다.)
341 * 그러므로 가능한 joinClientToChannel(), partClientFromChannel() 등 미리 구현된 함수를 사용해야합니다.
342 *
343 * ## [English]
344 * Even if WeakPtr is Expired, it is not automatically removed from the map.
345 * When directly manipulating the map, you should be aware of the following issues.
346 * If an expired entry is not removed and an attempt is made to map::insert() with the same key, it will not be overwritten. (map::insert() fails if the same key exists.)
347 * To prevent this, you must manually remove the expired entry or use map::operator[] instead of map::insert(). (operator[] overwrites if the same key exists.)
348 * Thus, you should use pre-implemented functions such as joinClientToChannel(), partClientFromChannel().
349 */
350 std::map< std::string, WeakPtr< ChannelControlBlock > > mChannels;
351 };
352
353} // namespace irc
#define FORCEINLINE
Definition AttributeDefines.hpp:55
#define UNUSED
Definition AttributeDefines.hpp:30
#define IRC_CLIENT_COMMAND_LIST
Definition ClientCommand.hpp:12
struct kevent kevent_t
Definition SocketTypedef.hpp:9
Definition Server.hpp:101
EIrcErrorCode separateMsgsFromClientRecvMsgs(SharedPtr< ClientControlBlock > client, std::vector< SharedPtr< MsgBlock > > &outSeparatedMsgs)
Separate all separable messages in the client's ClientControlBlock::RecvMsgBlocks.
Definition Server.cpp:485
EIrcErrorCode eventLoop()
A main event loop of the server.
Definition Server.cpp:119
std::vector< kevent_t > mEventRegistrationQueue
Definition Server.hpp:312
EIrcErrorCode Startup()
Initialize resources and start the server event loop.
Definition Server.cpp:54
void logMessage(const std::string &message) const
Definition Server.cpp:1012
std::map< std::string, SharedPtr< ClientControlBlock > > mClients
Nickname to client map.
Definition Server.hpp:323
EIrcErrorCode disconnectClient(SharedPtr< ClientControlBlock > client, const std::string quitMessage="")
Mark the client's bExpired flag and block the messages from the client, then close the socket after r...
Definition Server.cpp:791
SharedPtr< ClientControlBlock > findClientGlobal(const std::string &nickname)
Definition Server.cpp:896
EIrcErrorCode processClientMsg(SharedPtr< ClientControlBlock > client, SharedPtr< MsgBlock > msg)
Execute and reply the client's single message.
Definition Server.cpp:582
bool registerClient(SharedPtr< ClientControlBlock > client)
Register a client to the server.
Definition Server.cpp:836
void logErrorCode(EIrcErrorCode errorCode) const
Definition Server.cpp:1007
void sendMsgToChannel(SharedPtr< ChannelControlBlock > channel, SharedPtr< MsgBlock > msg, SharedPtr< ClientControlBlock > exceptClient=NULL)
Send a message to channel members.
Definition Server.cpp:973
UNUSED Server & operator=(const Server &rhs)
Definition Server.cpp:15
std::vector< SharedPtr< ClientControlBlock > > mUnregistedClients
Nickname to client map.
Definition Server.hpp:320
std::string mServerPassword
Definition Server.hpp:279
void joinClientToChannel(SharedPtr< ClientControlBlock > client, SharedPtr< ChannelControlBlock > channel)
Join a client to the exist channel without any error/permission check.
Definition Server.cpp:883
static EIrcErrorCode CreateServer(Server **outPtrServer, const std::string &serverName, const unsigned short port, const std::string &password)
Create a server instance.
Definition Server.cpp:21
void partClientFromChannel(SharedPtr< ClientControlBlock > client, SharedPtr< ChannelControlBlock > channel)
Part a client from the channel without any error/permission check.
Definition Server.cpp:889
IRC::EIrcErrorCode(Server::*) ClientCommandFuncPtr(SharedPtr< ClientControlBlock > client, const std::vector< char * > &arguments)
Client command execution function type.
Definition Server.hpp:174
EIrcErrorCode destroyResources()
Destroy all resources of the server.
Definition Server.cpp:439
void logVerbose(const std::string &message) const
Definition Server.cpp:1017
SharedPtr< ClientControlBlock > getClientFromKeventUdata(kevent_t &event) const
Get SharedPtr to the ClientControlBlock from the kevent's udata.
Definition Server.hpp:164
Server(const std::string &serverName, const unsigned short port, const std::string &password)
Definition Server.cpp:44
short mServerPort
Definition Server.hpp:278
int mhKqueue
Definition Server.hpp:311
int mhListenSocket
Definition Server.hpp:281
void sendMsgToClient(SharedPtr< ClientControlBlock > client, SharedPtr< MsgBlock > msg)
Send a message to client.
Definition Server.cpp:929
std::vector< SharedPtr< ClientControlBlock > > mClientReleaseQueue
Queue to release expired clients.
Definition Server.hpp:330
~Server()
Definition Server.cpp:10
SharedPtr< ChannelControlBlock > findChannelGlobal(const std::string &channelName)
Definition Server.cpp:912
void sendMsgToConnectedChannels(SharedPtr< ClientControlBlock > client, SharedPtr< MsgBlock > msg)
Send a message to channels the client is connected.
Definition Server.cpp:990
std::map< std::string, WeakPtr< ChannelControlBlock > > mChannels
Channel name to channel map.
Definition Server.hpp:350
EIrcErrorCode forceDisconnectClient(SharedPtr< ClientControlBlock > client, const std::string quitMessage="")
Close the client socket and release the client.
Definition Server.cpp:713
std::string mServerName
Definition Server.hpp:277
Shared pointer custom implementation for C++98 standard.
Definition SharedPtr.hpp:164
Definition ControlBlock.hpp:7
Definition ChannelControlBlock.hpp:12
EIrcErrorCode
Error Codes Enum.
Definition IrcErrorCode.hpp:49
Do not use this class directly.
Definition SharedPtr.hpp:31