Update PlayerbotCommandServer.cpp

Protection against race conditions: Added a global mutex (session_mutex) to synchronize access to the session() function when multiple threads are running. This avoids concurrency problems in environments with many simultaneous connections.

Thread pool usage: Replaced manual thread creation with boost::thread with a thread pool using boost::asio::thread_pool. The thread pool improves resource management and limits the number of simultaneous threads, optimizing performance.

Checking for socket errors: Added check to ensure the socket is not null or closed before trying to read it.
Implemented detailed error handling for readings and writings, including detailed logs to facilitate debugging.
Secure socket closure: Now sockets are correctly closed at the end of the session, even in the event of an error, preventing resource leaks.
This commit is contained in:
EricksOliveira
2024-10-18 14:37:54 -03:00
parent 9280188841
commit 8f2455b766

View File

@@ -9,8 +9,10 @@
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/thread/thread.hpp>
#include <boost/asio/thread_pool.hpp>
#include <cstdlib>
#include <iostream>
#include <mutex>
#include "IoContext.h"
#include "Playerbots.h"
@@ -18,21 +20,39 @@
using boost::asio::ip::tcp;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
std::mutex session_mutex; // Global mutex to protect sessions
boost::asio::thread_pool pool(4); // 4-thread thread pool
bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line)
{
// Do the real reading from fd until buffer has '\n'.
// Check if the socket is valid before using it
if (!sock || !sock->is_open())
{
LOG_ERROR("playerbots", "Invalid or closed socket.");
return false;
}
// Does the actual reading until the buffer has a '\n'
std::string::iterator pos;
while ((pos = find(buffer->begin(), buffer->end(), '\n')) == buffer->end())
{
char buf[1025];
boost::system::error_code error;
size_t n = sock->read_some(boost::asio::buffer(buf), error);
if (n == -1 || error == boost::asio::error::eof)
return false;
else if (error)
throw boost::system::system_error(error); // Some other error.
buf[n] = 0;
// Read socket data
size_t n = sock->read_some(boost::asio::buffer(buf), error);
if (n == 0 || error == boost::asio::error::eof)
{
LOG_INFO("playerbots", "Connection closed by peer.");
return false;
}
else if (error)
{
LOG_ERROR("playerbots", "Socket read error: {}", error.message());
return false; // Returns false in case of error.
}
buf[n] = 0; // Ensures the buffer ends with null
*buffer += buf;
}
@@ -45,6 +65,8 @@ void session(socket_ptr sock)
{
try
{
std::lock_guard<std::mutex> guard(session_mutex); // Protect session with mutex
std::string buffer, request;
while (ReadLine(sock, &buffer, &request))
{
@@ -55,7 +77,19 @@ void session(socket_ptr sock)
}
catch (std::exception& e)
{
LOG_ERROR("playerbots", "{}", e.what());
LOG_ERROR("playerbots", "Session error: {}", e.what());
}
// Make sure to close the socket at the end of the session
if (sock && sock->is_open())
{
boost::system::error_code ec;
sock->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
sock->close(ec);
if (ec)
{
LOG_ERROR("playerbots", "Error closing socket: {}", ec.message());
}
}
}
@@ -66,7 +100,7 @@ void server(Acore::Asio::IoContext& io_service, short port)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(session, sock));
boost::asio::post(pool, boost::bind(session, sock)); // Use thread pool instead of creating new threads
}
}