@@ -26,6 +26,10 @@ rconpp::rcon_server::~rcon_server() {
2626 if (accept_connections_runner.joinable ()) {
2727 accept_connections_runner.join ();
2828 }
29+
30+ if (heartbeat_runner.joinable ()) {
31+ heartbeat_runner.join ();
32+ }
2933}
3034
3135bool rconpp::rcon_server::startup_server () {
@@ -98,71 +102,70 @@ void rconpp::rcon_server::disconnect_client(const int client_socket) {
98102}
99103
100104void 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);
105+ const int packet_size = read_packet_size (static_cast <int >(sock), on_log);
103106
104- if (packet_size <= MIN_PACKET_SIZE) {
105- continue ;
106- }
107+ if (packet_size <= MIN_PACKET_SIZE) {
108+ return ;
109+ }
107110
108- std::vector<char > buffer{};
109- buffer.resize (packet_size);
111+ std::vector<char > buffer{};
112+ buffer.resize (packet_size);
110113
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- }
114+ if (recv (client.socket , buffer.data (), packet_size, 0 ) == -1 ) {
115+ on_log (" Failed to get a packet from client." );
116+ report_error ();
117+ return ;
118+ }
115119
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);
120+ std::string packet_data (&buffer[8 ], &buffer[buffer.size ()-2 ]);
121+ int id = bit32_to_int (buffer);
122+ int type = type_to_int (buffer);
119123
120- rconpp::packet packet_to_send{};
124+ rconpp::packet packet_to_send{};
121125
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- }
126+ if (!client.authenticated ) {
127+ if (packet_data == password) {
128+ packet_to_send = form_packet (" " , id, rconpp::data_type::SERVERDATA_AUTH_RESPONSE);
129+ client.authenticated = true ;
129130 } 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." );
131+ packet_to_send = form_packet (" " , -1 , rconpp::data_type::SERVERDATA_AUTH_RESPONSE);
132+ }
133+ } 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);
136+ 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." );
137+ } else {
138+ 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 + " \" " );
139+ if (!on_command) {
140+ on_log (" You have not set any response for on_command! The server will default to a blank response." );
141+
142+ /*
143+ * Whilst sending information about the server not responding would be nice,
144+ * we would end up with the possibility of clients thinking that is the response.
145+ * It's better to just send no information and let clients assume that meant
146+ * the server didn't like the command.
147+ */
148+ packet_to_send = form_packet (" " , id, rconpp::data_type::SERVERDATA_RESPONSE_VALUE);
133149 } 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- }
150+ client_command command{};
151+ command.command = packet_data;
152+ command.client = client;
153+
154+ std::string text_to_send = on_command (command);
155+
156+ 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 )) + " ]." );
157+
158+ packet_to_send = form_packet (text_to_send, id, rconpp::data_type::SERVERDATA_RESPONSE_VALUE);
156159 }
157160 }
161+ }
158162
159- on_log (" Sending... " );
163+ 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 )) + " ] " );
160164
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- }
165+ if (send (client.socket , packet_to_send.data .data (), packet_to_send.length , 0 ) < 0 ) {
166+ on_log (" Sending failed!" );
167+ report_error ();
168+ return ;
166169 }
167170}
168171
@@ -209,14 +212,34 @@ void rconpp::rcon_server::start(bool return_after) {
209212 client.connected = true ;
210213
211214 std::thread client_thread ([this , client]{
212- read_packet (client);
215+ while (client.connected ) {
216+ read_packet (client);
217+
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 );
223+
224+ if (current_time - last_time >= HEARTBEAT_TIME)
225+ {
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.
230+ }
231+ }
232+
233+ // No need to let the server keep running this causing 100% usage on a thread, we can wait a bit between requests.
234+ std::this_thread::sleep_for (std::chrono::milliseconds (100 ));
235+ }
213236 });
214237
215238 request_handlers.insert ({ client_socket, std::move (client_thread) });
216239
217240 request_handlers.at (client_socket).detach ();
218241
219- connected_clients.insert ({});
242+ connected_clients.insert ({ client_socket, client });
220243 }
221244 });
222245
0 commit comments