@@ -7,14 +7,18 @@ rconpp::rcon_server::rcon_server(const std::string_view addr, const int _port, c
77}
88
99rconpp::rcon_server::~rcon_server () {
10+ if (on_log) {
11+ on_log (" RCON server is shutting down." );
12+ }
13+
1014 // Set connected to false, meaning no requests can be attempted during shutdown.
1115 online = false ;
1216
1317 terminating.notify_all ();
1418
1519 // Safely disconnect all clients from server.
1620 for (const auto & client : connected_clients) {
17- disconnect_client (client.first );
21+ disconnect_client (client.first , false );
1822 }
1923
2024#ifdef _WIN32
@@ -26,10 +30,6 @@ rconpp::rcon_server::~rcon_server() {
2630 if (accept_connections_runner.joinable ()) {
2731 accept_connections_runner.join ();
2832 }
29-
30- if (heartbeat_runner.joinable ()) {
31- heartbeat_runner.join ();
32- }
3333}
3434
3535bool rconpp::rcon_server::startup_server () {
@@ -84,27 +84,32 @@ bool rconpp::rcon_server::startup_server() {
8484 return true ;
8585}
8686
87- void rconpp::rcon_server::disconnect_client (const int client_socket) {
87+ void rconpp::rcon_server::disconnect_client (const int client_socket, const bool remove_after /* = true */ ) {
8888
8989#ifdef _WIN32
9090 closesocket (client_socket);
9191#else
9292 close (client_socket);
9393#endif
9494
95+ std::lock_guard guard (connected_clients_mutex);
96+
9597 connected_clients.at (client_socket).connected = false ;
9698
9799 if (request_handlers.at (client_socket).joinable ()) {
98100 request_handlers.at (client_socket).join ();
99101 }
100102
101- connected_clients.erase (client_socket);
103+ if (remove_after) {
104+ connected_clients.erase (client_socket);
105+ }
102106}
103107
104- void rconpp::rcon_server::read_packet (rconpp:: connected_client client) {
105- const int packet_size = read_packet_size (static_cast <int >(sock), on_log );
108+ void rconpp::rcon_server::read_packet (connected_client& client) {
109+ const int packet_size = read_packet_size (static_cast <int >(client. socket ) );
106110
107- if (packet_size <= MIN_PACKET_SIZE ) {
111+ // Silently ignore packet size.
112+ if (packet_size < MIN_PACKET_SIZE ) {
108113 return ;
109114 }
110115
@@ -117,22 +122,26 @@ void rconpp::rcon_server::read_packet(rconpp::connected_client client) {
117122 return ;
118123 }
119124
125+ // Client is talking to us, we don't need to send a heartbeat if we're being talked to.
126+ client.last_heartbeat = time (nullptr );
127+
120128 std::string packet_data (&buffer[8 ], &buffer[buffer.size ()-2 ]);
121129 int id = bit32_to_int (buffer);
122130 int type = type_to_int (buffer);
123131
124- rconpp:: packet packet_to_send{};
132+ packet packet_to_send{};
125133
126134 if (!client.authenticated ) {
135+ on_log (" Client not authenticated, handling authentication." );
127136 if (packet_data == password) {
128- packet_to_send = form_packet (" " , id, rconpp::data_type:: SERVERDATA_AUTH_RESPONSE );
137+ packet_to_send = form_packet (" " , id, SERVERDATA_AUTH_RESPONSE );
129138 client.authenticated = true ;
130139 } else {
131- packet_to_send = form_packet (" " , -1 , rconpp::data_type:: SERVERDATA_AUTH_RESPONSE );
140+ packet_to_send = form_packet (" " , -1 , SERVERDATA_AUTH_RESPONSE );
132141 }
133142 } else {
134- if (type != rconpp::data_type:: SERVERDATA_EXECCOMMAND ) {
135- packet_to_send = form_packet (" Invalid packet type (" + std::to_string (type) + " ). Double check your packets." , id, rconpp::data_type:: SERVERDATA_RESPONSE_VALUE );
143+ if (type != SERVERDATA_EXECCOMMAND ) {
144+ packet_to_send = form_packet (" Invalid packet type (" + std::to_string (type) + " ). Double check your packets." , id, SERVERDATA_RESPONSE_VALUE );
136145 on_log (" Invalid packet type (" + std::to_string (type) + " ) sent by [" + inet_ntoa (client.sock_info .sin_addr ) + " :" + std::to_string (ntohs (client.sock_info .sin_port )) + " ]. Double check your packets." );
137146 } else {
138147 on_log (" Client [" + std::string (inet_ntoa (client.sock_info .sin_addr )) + " :" + std::to_string (ntohs (client.sock_info .sin_port )) + " ] has asked to execute the command: \" " + packet_data + " \" " );
@@ -145,7 +154,7 @@ void rconpp::rcon_server::read_packet(rconpp::connected_client client) {
145154 * It's better to just send no information and let clients assume that meant
146155 * the server didn't like the command.
147156 */
148- packet_to_send = form_packet (" " , id, rconpp::data_type:: SERVERDATA_RESPONSE_VALUE );
157+ packet_to_send = form_packet (" " , id, SERVERDATA_RESPONSE_VALUE );
149158 } else {
150159 client_command command{};
151160 command.command = packet_data;
@@ -155,7 +164,7 @@ void rconpp::rcon_server::read_packet(rconpp::connected_client client) {
155164
156165 on_log (" Sending reply \" " + text_to_send + " \" to client [" + std::string (inet_ntoa (client.sock_info .sin_addr )) + " :" + std::to_string (ntohs (client.sock_info .sin_port )) + " ]." );
157166
158- packet_to_send = form_packet (text_to_send, id, rconpp::data_type:: SERVERDATA_RESPONSE_VALUE );
167+ packet_to_send = form_packet (text_to_send, id, SERVERDATA_RESPONSE_VALUE );
159168 }
160169 }
161170 }
@@ -169,6 +178,21 @@ void rconpp::rcon_server::read_packet(rconpp::connected_client client) {
169178 }
170179}
171180
181+ bool rconpp::rcon_server::send_heartbeat (connected_client& client) {
182+ on_log (" Sending heartbeat to client [" + std::string (inet_ntoa (client.sock_info .sin_addr )) + " :" + std::to_string (ntohs (client.sock_info .sin_port )) + " ]" );
183+
184+ packet packet_to_send = form_packet (" " , -1 , SERVERDATA_RESPONSE_VALUE );
185+ if (send (client.socket , packet_to_send.data .data (), packet_to_send.length , 0 ) < 0 ) {
186+ on_log (" Failed to send a heartbeat to client [" + std::string (inet_ntoa (client.sock_info .sin_addr )) + " :" + std::to_string (ntohs (client.sock_info .sin_port )) + " ]" );
187+ report_error ();
188+ return false ;
189+ }
190+
191+ client.last_heartbeat = time (nullptr );
192+
193+ return true ;
194+ }
195+
172196void rconpp::rcon_server::start (bool return_after) {
173197 auto block_calling_thread = [this ]() {
174198 std::mutex thread_mutex;
@@ -184,7 +208,7 @@ void rconpp::rcon_server::start(bool return_after) {
184208 on_log (" Attempting to startup an RCON server..." );
185209
186210 if (!startup_server ()) {
187- on_log (" RCON++ is aborting as it failed to initiate server." );
211+ on_log (" RCON server is aborting as it failed to initiate server." );
188212 return ;
189213 }
190214
@@ -194,39 +218,37 @@ void rconpp::rcon_server::start(bool return_after) {
194218
195219 accept_connections_runner = std::thread ([this ]() {
196220 while (online) {
197- connected_client client{};
198- struct sockaddr_in client_info{};
221+ sockaddr_in client_info{};
199222
200223 socklen_t client_len = sizeof (client_info);
201224 int client_socket = accept (sock, reinterpret_cast <sockaddr*>(&client_info), &client_len);
202225
203226 if (client_socket == -1 ) {
204227 on_log (" client with socket: \" " + std::to_string (client_socket) + " \" failed to connect." );
228+ report_error ();
205229 continue ;
206230 }
207231
208232 on_log (" Client [" + std::string (inet_ntoa (client_info.sin_addr )) + " :" + std::to_string (ntohs (client_info.sin_port )) + " ] has connected to the server." );
209233
234+ connected_client client{};
235+
210236 client.sock_info = client_info;
211237 client.socket = client_socket;
212238 client.connected = true ;
213239
214- std::thread client_thread ([this , client]{
240+ std::thread client_thread ([this , & client]{
215241 while (client.connected ) {
216242 read_packet (client);
217243
218- time_t current_time = time (nullptr );
219- if (client_socket_to_last_heartbeat.find (client.socket ) == client_socket_to_last_heartbeat.end ()) {
220- client_socket_to_last_heartbeat.insert ({ client.socket , current_time });
221- } else {
222- time_t last_time = client_socket_to_last_heartbeat.at (client.socket );
244+ const time_t current_time = time (nullptr );
223245
224- if (current_time - last_time >= HEARTBEAT_TIME )
246+ if (client.authenticated ) {
247+ if (client.last_heartbeat == 0 || current_time - client.last_heartbeat >= HEARTBEAT_TIME )
225248 {
226- send_heartbeat (client);
227-
228- // We should check if the heartbeat actually got anything, if it does then insert back into the map.
229- // if it failed, we bin that client off and shut this thread down.
249+ if (!send_heartbeat (client)) {
250+ disconnect_client (client.socket );
251+ }
230252 }
231253 }
232254
@@ -239,6 +261,8 @@ void rconpp::rcon_server::start(bool return_after) {
239261
240262 request_handlers.at (client_socket).detach ();
241263
264+ std::lock_guard guard (connected_clients_mutex);
265+
242266 connected_clients.insert ({ client_socket, client });
243267 }
244268 });
0 commit comments