Skip to content

Commit d418cb2

Browse files
committed
feat: gracefully shutdown socket
1 parent 4183815 commit d418cb2

7 files changed

+92
-3
lines changed

include/socket.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace tpt
2020
virtual void sendData(SOCKET client_socket, const void *buffer, unsigned int buffer_size, int flags) = 0;
2121
virtual void closeSocket() = 0;
2222
virtual void closeSocket(SOCKET client_socket) = 0;
23+
virtual void setSocketTimeout(SOCKET sock, int timeoutSec) = 0;
2324
};
2425
}
2526

include/teapot.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#endif
3232

3333
#ifdef _WIN32
34+
#include <windows.h>
3435
#include "win_socket.hpp"
3536
#endif
3637

include/unix_socket.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace tpt
2929
std::string ip_address;
3030
std::string client_ip;
3131
std::vector<std::string> ip_blacklist;
32+
std::vector<SOCKET> client_sockets;
3233
ConsoleLogger logger;
3334

3435
public:
@@ -46,6 +47,7 @@ namespace tpt
4647
virtual void sendData(SOCKET client_socket, const void *buffer, unsigned int buffer_size, int flags) override;
4748
virtual void closeSocket() override;
4849
virtual void closeSocket(SOCKET client_socket) override;
50+
virtual void setSocketTimeout(SOCKET sock, int timeoutSec) override;
4951
};
5052
}
5153

include/win_socket.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ namespace tpt
4949
virtual void sendData(SOCKET client_socket, const void *buffer, unsigned int buffer_size, int flags) override;
5050
virtual void closeSocket() override;
5151
virtual void closeSocket(SOCKET client_socket) override;
52+
virtual void setSocketTimeout(SOCKET sock, int timeoutSec) override;
5253
};
5354
}
5455

src/teapot.cpp

+38-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,27 @@
33

44
using namespace tpt;
55

6+
volatile std::sig_atomic_t signal_received = 0;
7+
8+
#ifdef __linux__
9+
extern "C" void signalHandler(int signal)
10+
{
11+
signal_received = signal;
12+
}
13+
#endif
14+
15+
#ifdef _WIN32
16+
BOOL WINAPI consoleHandler(DWORD signal)
17+
{
18+
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT)
19+
{
20+
signal_received = 1;
21+
return TRUE;
22+
}
23+
return FALSE;
24+
}
25+
#endif
26+
627
std::optional<Request> Teapot::parseRequest(int client_socket)
728
{
829
char buffer[BUFFER_SIZE] = {0};
@@ -212,9 +233,21 @@ Teapot::Teapot(std::string ip_address, unsigned int port, unsigned int max_conne
212233

213234
void Teapot::run()
214235
{
236+
#ifdef __linux__
237+
std::signal(SIGINT, signalHandler);
238+
std::signal(SIGTERM, signalHandler);
239+
#endif
240+
#ifdef _WIN32
241+
if (!SetConsoleCtrlHandler(consoleHandler, TRUE))
242+
{
243+
std::cerr << "ERROR: Could not set control handler" << std::endl;
244+
return;
245+
}
246+
#endif
247+
215248
socket.bindSocket();
216249

217-
while (true)
250+
while (!signal_received)
218251
{
219252
socket.listenToConnections();
220253

@@ -233,6 +266,10 @@ void Teapot::run()
233266
this->socket.closeSocket(client_socket);
234267
}
235268
}
269+
270+
LOG_INFO(logger, "Shutting down...");
271+
this->socket.closeSocket();
272+
sleep(3);
236273
}
237274

238275
void Teapot::serveFile(std::string url, std::string file_path)

src/unix_socket.cpp

+33-2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ void UnixSocket::bindSocket()
133133
std::cout << "Error code: " + errno << std::endl;
134134
exit(EXIT_FAILURE);
135135
}
136+
this->setSocketTimeout(this->server_socket, 5);
136137
LOG_INFO(logger, "Binding done! Listening to connections ...");
137138
}
138139

@@ -188,6 +189,8 @@ void UnixSocket::acceptConnection(SOCKET &client_socket, void *client_address)
188189
throw IPBlackListedException();
189190
}
190191
}
192+
193+
this->client_sockets.push_back(client_socket);
191194
}
192195

193196
ssize_t UnixSocket::receiveData(SOCKET client_socket, char *buffer, unsigned int buffer_size)
@@ -209,7 +212,13 @@ void UnixSocket::sendData(SOCKET client_socket, const void *buffer, unsigned int
209212

210213
void UnixSocket::closeSocket()
211214
{
212-
std::cout << "Closing socket ..." << std::endl;
215+
LOG_INFO(logger, "Cleaning up client sockets ...");
216+
for (auto &it : this->client_sockets)
217+
{
218+
this->closeSocket(it);
219+
}
220+
221+
LOG_INFO(logger, "Closing socket ...");
213222
if (shutdown(this->server_socket, SHUT_RDWR) == -1)
214223
{
215224
perror("An error occurred while shutting down the socket: ");
@@ -218,7 +227,7 @@ void UnixSocket::closeSocket()
218227
}
219228
if (close(this->server_socket) == 0)
220229
{
221-
std::cout << "Socket closed!" << std::endl;
230+
LOG_INFO(logger, "Socket closed!");
222231
exit(EXIT_SUCCESS);
223232
}
224233
else
@@ -231,14 +240,36 @@ void UnixSocket::closeSocket()
231240

232241
void UnixSocket::closeSocket(SOCKET client_socket)
233242
{
243+
if (shutdown(client_socket, SHUT_RDWR) == -1)
244+
{
245+
perror("An error occurred while shutting down the socket: ");
246+
std::cout << "Error code: " + errno << std::endl;
247+
exit(EXIT_FAILURE);
248+
}
234249
close(client_socket);
250+
251+
auto it = std::find(this->client_sockets.begin(), this->client_sockets.end(), client_socket);
252+
253+
// Check if element was found before erasing
254+
if (it != this->client_sockets.end())
255+
{
256+
this->client_sockets.erase(it);
257+
}
235258
}
236259

237260
std::string UnixSocket::getClientIp()
238261
{
239262
return this->client_ip;
240263
}
241264

265+
void UnixSocket::setSocketTimeout(SOCKET sock, int timeoutSec)
266+
{
267+
struct timeval tv;
268+
tv.tv_sec = timeoutSec; // Seconds
269+
tv.tv_usec = 0; // Microseconds
270+
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv);
271+
}
272+
242273
UnixSocket::~UnixSocket() {}
243274

244275
#endif

src/win_socket.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ void WinSocket::bindSocket()
162162
WSACleanup();
163163
exit(EXIT_FAILURE);
164164
}
165+
this->setSocketTimeout(this->server_socket, 5);
165166
std::cout << "Binding done!" << std::endl;
166167
std::cout << "Listening to connections ..." << std::endl;
167168
}
@@ -249,6 +250,21 @@ void WinSocket::closeSocket(SOCKET client_socket)
249250
closesocket(client_socket);
250251
}
251252

253+
void WinSocket::setSocketTimeout(SOCKET sock, int timeoutSec)
254+
{
255+
// Initialize the timeout value
256+
DWORD timeout = timeoutSec * 1000; // Convert to milliseconds, as required by Windows
257+
258+
// Set the receive timeout option
259+
int result = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));
260+
if (result == SOCKET_ERROR)
261+
{
262+
std::printf("Error setting timeout: %d\n", WSAGetLastError());
263+
WSACleanup();
264+
exit(EXIT_FAILURE);
265+
}
266+
}
267+
252268
WinSocket::~WinSocket() {}
253269

254270
#endif

0 commit comments

Comments
 (0)