@@ -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
@@ -80,90 +84,113 @@ bool rconpp::rcon_server::startup_server() {
8084 return true ;
8185}
8286
83- 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 */ ) {
8488
8589#ifdef _WIN32
8690 closesocket (client_socket);
8791#else
8892 close (client_socket);
8993#endif
9094
95+ std::lock_guard guard (connected_clients_mutex);
96+
9197 connected_clients.at (client_socket).connected = false ;
9298
9399 if (request_handlers.at (client_socket).joinable ()) {
94100 request_handlers.at (client_socket).join ();
95101 }
96102
97- connected_clients.erase (client_socket);
103+ if (remove_after) {
104+ connected_clients.erase (client_socket);
105+ }
98106}
99107
100- void rconpp::rcon_server::read_packet (rconpp::connected_client client) {
101- while (client.connected ) {
102- 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 ));
103110
104- if (packet_size <= MIN_PACKET_SIZE) {
105- continue ;
106- }
111+ // Silently ignore packet size.
112+ if (packet_size < MIN_PACKET_SIZE) {
113+ return ;
114+ }
107115
108- std::vector<char > buffer{};
109- buffer.resize (packet_size);
116+ std::vector<char > buffer{};
117+ buffer.resize (packet_size);
110118
111- if (recv (client.socket , buffer.data (), packet_size, 0 ) == -1 ) {
112- on_log (" Failed to get a packet from client." );
113- report_error ();
114- }
119+ if (recv (client.socket , buffer.data (), packet_size, 0 ) == -1 ) {
120+ on_log (" Failed to get a packet from client." );
121+ report_error ();
122+ return ;
123+ }
115124
116- std::string packet_data (&buffer[8 ], &buffer[buffer.size ()-2 ]);
117- int id = bit32_to_int (buffer);
118- int type = type_to_int (buffer);
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 );
119127
120- rconpp::packet packet_to_send{};
128+ std::string packet_data (&buffer[8 ], &buffer[buffer.size ()-2 ]);
129+ int id = bit32_to_int (buffer);
130+ int type = type_to_int (buffer);
121131
122- if (!client.authenticated ) {
123- if (packet_data == password) {
124- packet_to_send = form_packet (" " , id, rconpp::data_type::SERVERDATA_AUTH_RESPONSE);
125- client.authenticated = true ;
126- } else {
127- packet_to_send = form_packet (" " , -1 , rconpp::data_type::SERVERDATA_AUTH_RESPONSE);
128- }
132+ packet packet_to_send{};
133+
134+ if (!client.authenticated ) {
135+ on_log (" Client not authenticated, handling authentication." );
136+ if (packet_data == password) {
137+ packet_to_send = form_packet (" " , id, SERVERDATA_AUTH_RESPONSE);
138+ client.authenticated = true ;
139+ } else {
140+ packet_to_send = form_packet (" " , -1 , SERVERDATA_AUTH_RESPONSE);
141+ }
142+ } else {
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);
145+ 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." );
129146 } else {
130- if (type != rconpp::data_type::SERVERDATA_EXECCOMMAND) {
131- packet_to_send = form_packet (" Invalid packet type (" + std::to_string (type) + " ). Double check your packets." , id, rconpp::data_type::SERVERDATA_RESPONSE_VALUE);
132- 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." );
147+ 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 + " \" " );
148+ if (!on_command) {
149+ on_log (" You have not set any response for on_command! The server will default to a blank response." );
150+
151+ /*
152+ * Whilst sending information about the server not responding would be nice,
153+ * we would end up with the possibility of clients thinking that is the response.
154+ * It's better to just send no information and let clients assume that meant
155+ * the server didn't like the command.
156+ */
157+ packet_to_send = form_packet (" " , id, SERVERDATA_RESPONSE_VALUE);
133158 } else {
134- 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 + " \" " );
135- if (!on_command) {
136- on_log (" You have not set any response for on_command! The server will default to a blank response." );
137-
138- /*
139- * Whilst sending information about the server not responding would be nice,
140- * we would end up with the possibility of clients thinking that is the response.
141- * It's better to just send no information and let clients assume that meant
142- * the server didn't like the command.
143- */
144- packet_to_send = form_packet (" " , id, rconpp::data_type::SERVERDATA_RESPONSE_VALUE);
145- } else {
146- client_command command{};
147- command.command = packet_data;
148- command.client = client;
149-
150- std::string text_to_send = on_command (command);
151-
152- 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 )) + " ]." );
153-
154- packet_to_send = form_packet (text_to_send, id, rconpp::data_type::SERVERDATA_RESPONSE_VALUE);
155- }
159+ client_command command{};
160+ command.command = packet_data;
161+ command.client = client;
162+
163+ std::string text_to_send = on_command (command);
164+
165+ 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 )) + " ]." );
166+
167+ packet_to_send = form_packet (text_to_send, id, SERVERDATA_RESPONSE_VALUE);
156168 }
157169 }
170+ }
158171
159- on_log (" Sending... " );
172+ on_log (" Sending packet (of size: " + std::to_string (packet_to_send. length ) + " ) to client [ " + std::string ( inet_ntoa (client. sock_info . sin_addr )) + " : " + std::to_string ( ntohs (client. sock_info . sin_port )) + " ] " );
160173
161- if (send (client.socket , packet_to_send.data .data (), packet_to_send.length , 0 ) < 0 ) {
162- on_log (" Sending failed!" );
163- report_error ();
164- continue ;
165- }
174+ if (send (client.socket , packet_to_send.data .data (), packet_to_send.length , 0 ) < 0 ) {
175+ on_log (" Sending failed!" );
176+ report_error ();
177+ return ;
178+ }
179+ }
180+
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 ;
166189 }
190+
191+ client.last_heartbeat = time (nullptr );
192+
193+ return true ;
167194}
168195
169196void rconpp::rcon_server::start (bool return_after) {
@@ -181,7 +208,7 @@ void rconpp::rcon_server::start(bool return_after) {
181208 on_log (" Attempting to startup an RCON server..." );
182209
183210 if (!startup_server ()) {
184- on_log (" RCON++ is aborting as it failed to initiate server." );
211+ on_log (" RCON server is aborting as it failed to initiate server." );
185212 return ;
186213 }
187214
@@ -191,32 +218,52 @@ void rconpp::rcon_server::start(bool return_after) {
191218
192219 accept_connections_runner = std::thread ([this ]() {
193220 while (online) {
194- connected_client client{};
195- struct sockaddr_in client_info{};
221+ sockaddr_in client_info{};
196222
197223 socklen_t client_len = sizeof (client_info);
198224 int client_socket = accept (sock, reinterpret_cast <sockaddr*>(&client_info), &client_len);
199225
200226 if (client_socket == -1 ) {
201227 on_log (" client with socket: \" " + std::to_string (client_socket) + " \" failed to connect." );
228+ report_error ();
202229 continue ;
203230 }
204231
205232 on_log (" Client [" + std::string (inet_ntoa (client_info.sin_addr )) + " :" + std::to_string (ntohs (client_info.sin_port )) + " ] has connected to the server." );
206233
234+ connected_client client{};
235+
207236 client.sock_info = client_info;
208237 client.socket = client_socket;
209238 client.connected = true ;
210239
211- std::thread client_thread ([this , client]{
212- read_packet (client);
240+ std::thread client_thread ([this , &client]{
241+ while (client.connected ) {
242+ read_packet (client);
243+
244+ const time_t current_time = time (nullptr );
245+
246+ if (client.authenticated ) {
247+ if (client.last_heartbeat == 0 || current_time - client.last_heartbeat >= HEARTBEAT_TIME)
248+ {
249+ if (!send_heartbeat (client)) {
250+ disconnect_client (client.socket );
251+ }
252+ }
253+ }
254+
255+ // No need to let the server keep running this causing 100% usage on a thread, we can wait a bit between requests.
256+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
257+ }
213258 });
214259
215260 request_handlers.insert ({ client_socket, std::move (client_thread) });
216261
217262 request_handlers.at (client_socket).detach ();
218263
219- connected_clients.insert ({});
264+ std::lock_guard guard (connected_clients_mutex);
265+
266+ connected_clients.insert ({ client_socket, client });
220267 }
221268 });
222269
0 commit comments