ft_irc 1.0
読み取り中…
検索中…
一致する文字列を見つけられません
Server.cpp
[詳解]
1
8#include "Server.hpp"
9#include "Channel.hpp"
10#include "Client.hpp"
11#include "CommandManager.hpp"
12#include "Replies.hpp"
13#include <algorithm>
14#include <arpa/inet.h>
15#include <cerrno> // For errno, EWOULDBLOCK, EAGAIN
16#include <cstdlib>
17#include <cstring> // For strerror
18#include <ctime> // For time()
19#include <fcntl.h>
20#include <iostream>
21#include <map> // For std::map
22#include <netinet/in.h>
23#include <poll.h> // For struct pollfd
24#include <stdexcept>
25#include <string>
26#include <sys/socket.h>
27#include <unistd.h>
28#include <vector>
29
30// Define PING/PONG timeouts based on build type
31#ifdef TEST_BUILD
32#define PING_TIMEOUT 1 // Longer timeout for testing
33#define PONG_TIMEOUT 2
34#else
35#define PING_TIMEOUT 120 // Standard timeout for production
36#define PONG_TIMEOUT 20
37#endif
38
39// Define the static singleton instance
40Server *Server::_instance = NULL;
41
42// --- Testability Additions Implementation ---
44 delete _instance;
45 _instance = NULL;
46}
47// ネットワークを経由せず、テスト用のクライアントをサーバの管理下に追加する
49 if (client) {
50 _clients[client->getFd()] = client;
51
52 // _pollfdsにもダミーとして追加 (removeClientで使われるため)
53 struct pollfd pfd;
54 pfd.fd = client->getFd();
55 pfd.events = POLLIN;
56 pfd.revents = 0;
57 _pollfds.push_back(pfd);
58 }
59}
60// --------------------------------------------
61
62// Singleton accessor
63Server *Server::getInstance(int port, const std::string &password) {
64 if (_instance == NULL) {
65 _instance = new Server(port, password);
66 }
67 return _instance;
68}
69
70// Constructor
71Server::Server(int port, const std::string &password)
72 : _port(port), _password(password), _server_fd(-1), _serverName("irc.myserver.com") {
73 _commandManager = new CommandManager(this);
74}
75
76// Destructor
78 delete _commandManager;
79
80 // Delete all clients
81 for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it) {
82 close(it->first);
83 delete it->second;
84 }
85 _clients.clear();
86
87 // Delete all channels (if any were created)
88 for (std::map<std::string, Channel *>::iterator it = _channels.begin(); it != _channels.end(); ++it) {
89 delete it->second;
90 }
91 _channels.clear();
92
93 // Close server socket
94 if (_server_fd != -1) {
95 close(_server_fd);
96 }
97 _instance = NULL;
98}
99
100// Public run method
102 setupSocket();
103 mainLoop();
104}
105
106// Private: Setup the listening socket
107void Server::setupSocket() {
108 // Create socket
109 _server_fd = socket(AF_INET, SOCK_STREAM, 0);
110 if (_server_fd < 0) {
111 throw std::runtime_error("Failed to create socket");
112 }
113
114 // Set socket to be reusable
115 int opt = 1;
116 if (setsockopt(_server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
117 throw std::runtime_error("Failed to set socket options");
118 }
119
120 // Set socket to non-blocking
121 if (fcntl(_server_fd, F_SETFL, O_NONBLOCK) < 0) {
122 throw std::runtime_error("Failed to set socket to non-blocking");
123 }
124
125 // Prepare the sockaddr_in structure
126 struct sockaddr_in server_addr;
127 server_addr.sin_family = AF_INET;
128 server_addr.sin_addr.s_addr = INADDR_ANY;
129 server_addr.sin_port = htons(_port);
130
131 // Bind
132 if (bind(_server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
133 throw std::runtime_error("Failed to bind to port");
134 }
135
136 // Listen
137 if (listen(_server_fd, 10) < 0) {
138 throw std::runtime_error("Failed to listen on socket");
139 }
140
141 // Add the server socket to the poll vector
142 struct pollfd pfd;
143 pfd.fd = _server_fd;
144 pfd.events = POLLIN;
145 pfd.revents = 0;
146 _pollfds.push_back(pfd);
147
148 std::cout << "Server listening on port " << _port << std::endl;
149}
150
151// Private: Main event loop
152void Server::mainLoop() {
153 while (g_running) {
154 // Update POLLOUT events based on client send buffers
155 for (size_t i = 0; i < _pollfds.size(); ++i) {
156 if (_pollfds[i].fd == _server_fd) {
157 // Server socket always only watches for POLLIN
158 continue;
159 }
160
161 // For client sockets, check send buffer
162 std::map<int, Client *>::iterator it = _clients.find(_pollfds[i].fd);
163 if (it != _clients.end()) {
164 Client *client = it->second;
165 if (!client->getSendBuffer().empty()) {
166 _pollfds[i].events |= POLLOUT; // Add POLLOUT if send buffer has data
167 } else {
168 _pollfds[i].events &= ~POLLOUT; // Remove POLLOUT if send buffer is empty
169 }
170 } else {
171 // クライアントが_clientsマップに見つからない場合 (例: 既に切断処理中)
172 // このケースでは、POLLOUTを明示的にクリアしておく。
173 _pollfds[i].events &= ~POLLOUT;
174 }
175 }
176
177 // Wait for an event
178 int poll_count = poll(&_pollfds[0], _pollfds.size(), 1000); // 1秒ごとにタイムアウトを設定
179 if (poll_count < 0 && g_running) {
180 std::cerr << "Poll error" << std::endl;
181 break;
182 }
183
184 // Check each file descriptor for events
185 for (size_t i = 0; i < _pollfds.size(); ++i) {
186 if (_pollfds[i].revents == 0) {
187 continue;
188 }
189
190 // Handle disconnect/error events first as they are critical
191 if (_pollfds[i].revents & (POLLHUP | POLLERR)) {
192 removeClient(_pollfds[i].fd);
193 --i; // Adjust loop counter
194 continue; // Move to the next fd
195 }
196
197 // If ready for writing, process output buffer
198 if (_pollfds[i].revents & POLLOUT) {
199 handleClientWrite(_pollfds[i].fd);
200 if (_clients.count(_pollfds[i].fd) && _clients[_pollfds[i].fd]->isMarkedForDisconnect()) {
201 removeClient(_pollfds[i].fd);
202 --i;
203 continue;
204 }
205 }
206 // If ready for reading, process input buffer.
207 // POLLOUT is handled first, but POLLIN can also be processed in the same loop iteration.
208 if (_pollfds[i].revents & POLLIN) {
209 if (_pollfds[i].fd == _server_fd) {
211 } else {
212 handleClientData(_pollfds[i].fd);
213 if (_clients.count(_pollfds[i].fd) && _clients[_pollfds[i].fd]->isMarkedForDisconnect()) {
214 removeClient(_pollfds[i].fd);
215 --i;
216 continue;
217 }
218 }
219 }
220 }
221 checkClientTimeouts(); // クライアントのタイムアウトをチェック
222 }
223}
224
225// Private: Check client timeouts and send PINGs or disconnect
226void Server::checkClientTimeouts() {
227 std::time_t currentTime = std::time(NULL);
228 std::vector<int> clientsToDisconnect;
229
230 for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it) {
231 Client *client = it->second;
232 if (!client->isRegistered()) {
233 if (currentTime - client->getLastActivityTime() > UNREGISTERED_CLIENT_TIMEOUT) {
234 clientsToDisconnect.push_back(client->getFd());
235 }
236 continue;
237 }
238
239 // 1. PING送信後のPONG応答タイムアウトチェック
240 if (client->getLastPingSentTime() != 0) { // PINGを送信済みの場合
241 if (currentTime - client->getLastPingSentTime() > PONG_TIMEOUT) {
242 clientsToDisconnect.push_back(client->getFd());
243 }
244 }
245 // 2. アイドルタイムアウト後のPING送信
246 else if (currentTime - client->getLastActivityTime() > PING_TIMEOUT) { // PINGをまだ送信していない場合
247 std::string pingMessage = "PING :" + _serverName;
248 client->sendMessage(pingMessage);
249 client->setLastPingSentTime(currentTime); // PING送信時刻を更新
250 std::cout << "Sent PING to client " << client->getNickname() << " (fd: " << client->getFd() << ")"
251 << std::endl;
252 }
253 }
254
255 // タイムアウトしたクライアントを切断
256 for (size_t i = 0; i < clientsToDisconnect.size(); ++i) {
257 removeClient(clientsToDisconnect[i]);
258 }
259}
260
261// Public: Accept a new client connection
263 // Find client in map
264 struct sockaddr_in client_addr;
265 socklen_t client_len = sizeof(client_addr);
266 int client_fd = accept(_server_fd, (struct sockaddr *)&client_addr, &client_len);
267 if (client_fd < 0) {
268 std::cerr << "Failed to accept new client" << std::endl;
269 return;
270 }
271
272 // Set client socket to non-blocking
273 fcntl(client_fd, F_SETFL, O_NONBLOCK);
274
275 // Add to poll vector
276 struct pollfd pfd;
277 pfd.fd = client_fd;
278 pfd.events = POLLIN;
279 pfd.revents = 0; // Initialize revents to 0
280 _pollfds.push_back(pfd);
281
282 // Create and store new Client object
283 std::string hostname = inet_ntoa(client_addr.sin_addr);
284 _clients[client_fd] = new Client(client_fd, hostname);
285
286 std::cout << "New connection from " << hostname << " on fd " << client_fd << std::endl;
287}
288
289// Public: Remove a client
291 // Find client in map
292 std::map<int, Client *>::iterator it = _clients.find(fd);
293 if (it != _clients.end()) {
294 Client *client = it->second;
295 std::cout << "Client " << (client->getNickname().empty() ? "anonymous" : client->getNickname())
296 << " (fd: " << fd << ") disconnected." << std::endl;
297
298 // Remove client from all channels they are in
299 const std::vector<Channel *> &channels = client->getChannels();
300 for (size_t i = 0; i < channels.size(); ++i) {
301 channels[i]->removeMember(client);
302 }
303
304 delete client; // Free Client object
305 _clients.erase(it); // Remove from map
306 }
307
308 // Find and remove from pollfds vector
309 for (size_t i = 0; i < _pollfds.size(); ++i) {
310 if (_pollfds[i].fd == fd) {
311 _pollfds.erase(_pollfds.begin() + i);
312 break;
313 }
314 }
315
316 close(fd); // Close the socket
317}
318
319// Private: Handle data received from a client
320void Server::handleClientData(int fd) {
321 char buffer[512];
322 ssize_t bytes_read = recv(fd, buffer, sizeof(buffer) - 1, 0);
323
324 if (bytes_read <= 0) {
325 // Connection closed or error
326 removeClient(fd);
327 } else {
328 // Process data
329 buffer[bytes_read] = '\0';
330 Client *client = getClient(fd);
331 if (client) {
332 client->appendBuffer(buffer);
333 client->updateActivityTime(); // クライアントからのデータ受信で活動時刻を更新
334 std::string line;
335 while (!(line = client->readLineFromBuffer()).empty()) {
336 // Here you would parse the line and execute a command
337 std::cout << "FD " << fd << " >> " << line << std::endl;
338 _commandManager->parseAndExecute(client, line);
339 // コマンド実行後も活動時刻を更新することで、より正確なアイドル状態を把握
340 // client->updateActivityTime(); // 必要であればここでも更新 (二重更新になる可能性あり)
341 }
342 }
343 }
344}
345
346// Private: Handle writing data to a client socket from its send buffer
347void Server::handleClientWrite(int fd) {
348 Client *client = getClient(fd);
349 if (!client) {
350 return;
351 }
352
353 const std::string &sendBuffer = client->getSendBuffer();
354 if (sendBuffer.empty()) {
355 return;
356 }
357
358 ssize_t bytes_sent = send(fd, sendBuffer.c_str(), sendBuffer.length(), 0);
359
360 if (bytes_sent < 0) {
361 if (errno == EWOULDBLOCK || errno == EAGAIN) {
362 // Socket buffer full, try again later
363 std::cerr << "EWOULDBLOCK/EAGAIN on send to fd " << fd << std::endl;
364 } else {
365 // Other error, disconnect client
366 std::cerr << "Error sending to fd " << fd << ": " << strerror(errno) << std::endl;
367 client->markForDisconnect("Send error.");
368 }
369 } else if (bytes_sent > 0) {
370 // Successfully sent some bytes, remove from buffer
371 client->removeSentData(bytes_sent);
372 std::cerr << "Sent " << bytes_sent << " bytes to fd " << fd
373 << ". Remaining in buffer: " << client->getSendBuffer().length() << std::endl;
374 }
375}
376
377// --- Other getters and methods ---
378
379const std::string &Server::getPassword() const { return _password; }
380
381const std::string &Server::getServerName() const { return _serverName; }
382
384 std::map<int, Client *>::iterator it = _clients.find(fd);
385 if (it != _clients.end()) {
386 return it->second;
387 }
388 return NULL;
389}
390
392 for (std::map<int, Client *>::iterator it = _clients.begin(); it != _clients.end(); ++it) {
393 if (it->second->getNickname() == nickname) {
394 return it->second;
395 }
396 }
397 return NULL;
398}
399
401 if (channel) {
402 _channels[channel->getName()] = channel;
403 }
404}
405
408 if (it != _channels.end()) {
409 return it->second;
410 }
411 return NULL;
412}
413
416 if (it != _channels.end()) {
417 delete it->second;
418 _channels.erase(it);
419 }
420}
421
422const std::map<std::string, Channel *> &Server::getChannels() const { return _channels; }
423
424const std::map<int, Client *> &Server::getClients() const { return _clients; }
Manages channel members and states.
Manages client connection and state.
Manages IRC commands and their execution.
Defines IRC numeric replies and error messages.
#define PONG_TIMEOUT
Definition Server.cpp:36
#define PING_TIMEOUT
Definition Server.cpp:35
Core IRC server implementation.
volatile bool g_running
Definition main.cpp:13
#define UNREGISTERED_CLIENT_TIMEOUT
Definition Server.hpp:31
T begin(T... args)
T bind(T... args)
T c_str(T... args)
チャンネルのメンバーと状態を管理するクラス。
Definition Channel.hpp:25
const std::string & getName() const
Definition Channel.cpp:22
Represents an IRC client connected to the server.
Definition Client.hpp:25
const std::string & getSendBuffer() const
Gets the client's send buffer content.
Definition Client.cpp:41
bool isRegistered() const
Checks if the client is registered.
Definition Client.cpp:33
const std::vector< Channel * > & getChannels() const
Gets the list of channels the client is in.
Definition Client.cpp:43
void updateActivityTime()
Updates the client's last activity time to the current time.
Definition Client.cpp:87
std::time_t getLastActivityTime() const
Gets the timestamp of the client's last activity.
Definition Client.cpp:45
void appendBuffer(const std::string &data)
Appends data to the client's receive buffer.
Definition Client.cpp:94
virtual void sendMessage(const std::string &message) const
Sends a message to the client by appending it to the send buffer.
Definition Client.cpp:135
const std::string & getNickname() const
Gets the client's nickname.
Definition Client.cpp:25
void setLastPingSentTime(std::time_t time)
Sets the timestamp of the last PING sent to the client.
Definition Client.cpp:89
std::time_t getLastPingSentTime() const
Gets the timestamp of the last PING sent to the client.
Definition Client.cpp:47
void markForDisconnect(const std::string &message)
Marks the client for disconnection with a specified message.
Definition Client.cpp:68
std::string readLineFromBuffer()
Reads a single line from the receive buffer.
Definition Client.cpp:118
int getFd() const
Gets the client's file descriptor.
Definition Client.cpp:23
void removeSentData(size_t bytes_sent)
Removes a specified number of bytes from the beginning of the send buffer.
Definition Client.cpp:109
Manages the registration, parsing, and execution of IRC commands.
void parseAndExecute(Client *client, const std::string &rawMessage)
Parses a raw message and executes the corresponding command.
Implements the core IRC server functionality as a Singleton.
Definition Server.hpp:49
Channel * getChannel(const std::string &name)
Retrieves a Channel object by its name.
Definition Server.cpp:406
Client * getClientByNickname(const std::string &nickname)
Retrieves a Client object by its nickname.
Definition Server.cpp:391
const std::map< int, Client * > & getClients() const
Gets a constant reference to the map of clients managed by the server.
Definition Server.cpp:424
const std::map< std::string, Channel * > & getChannels() const
Gets a constant reference to the map of channels managed by the server.
Definition Server.cpp:422
~Server()
Destroys the Server object, cleaning up all clients, channels, and the command manager.
Definition Server.cpp:77
static Server * getInstance(int port=-1, const std::string &password="")
Gets the single instance of the Server (Singleton pattern).
Definition Server.cpp:63
void removeChannel(const std::string &name)
Removes a channel from the server, deleting the Channel object.
Definition Server.cpp:414
const std::string & getPassword() const
Gets the server's password.
Definition Server.cpp:379
void removeClient(int fd)
Removes a client from the server, closing its socket and freeing resources.
Definition Server.cpp:290
void acceptNewClient()
Accepts a new incoming client connection.
Definition Server.cpp:262
const std::string & getServerName() const
Gets the server's name.
Definition Server.cpp:381
void addChannel(Channel *channel)
Adds a new channel to the server.
Definition Server.cpp:400
void addTestClient(Client *client)
Adds a client directly to the server's management for testing purposes.
Definition Server.cpp:48
static void resetInstance()
Resets the singleton instance of the Server for testing purposes.
Definition Server.cpp:43
Client * getClient(int fd)
Retrieves a Client object by its file descriptor.
Definition Server.cpp:383
void run()
Starts the IRC server, setting up the socket and entering the main event loop.
Definition Server.cpp:101
T clear(T... args)
T count(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T erase(T... args)
T find(T... args)
T push_back(T... args)
T size(T... args)
T strerror(T... args)
T time(T... args)