mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-07 12:51:08 +00:00
refactor(Core/Network): Port TrinityCore socket optimizations (#24384)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: Shauren <shauren@users.noreply.github.com>
This commit is contained in:
@@ -162,7 +162,7 @@ void AccountInfo::LoadResult(Field* fields)
|
|||||||
Utf8ToUpperOnlyLatin(Login);
|
Utf8ToUpperOnlyLatin(Login);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthSession::AuthSession(tcp::socket&& socket) :
|
AuthSession::AuthSession(IoContextTcpSocket&& socket) :
|
||||||
Socket(std::move(socket)), _status(STATUS_CHALLENGE), _build(0), _expversion(0) { }
|
Socket(std::move(socket)), _status(STATUS_CHALLENGE), _build(0), _expversion(0) { }
|
||||||
|
|
||||||
void AuthSession::Start()
|
void AuthSession::Start()
|
||||||
@@ -216,7 +216,7 @@ void AuthSession::CheckIpCallback(PreparedQueryResult result)
|
|||||||
AsyncRead();
|
AsyncRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthSession::ReadHandler()
|
SocketReadCallbackResult AuthSession::ReadHandler()
|
||||||
{
|
{
|
||||||
MessageBuffer& packet = GetReadBuffer();
|
MessageBuffer& packet = GetReadBuffer();
|
||||||
|
|
||||||
@@ -234,7 +234,7 @@ void AuthSession::ReadHandler()
|
|||||||
if (_status != itr->second.status)
|
if (_status != itr->second.status)
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 size = uint16(itr->second.packetSize);
|
uint16 size = uint16(itr->second.packetSize);
|
||||||
@@ -248,7 +248,7 @@ void AuthSession::ReadHandler()
|
|||||||
if (size > MAX_ACCEPTED_CHALLENGE_SIZE)
|
if (size > MAX_ACCEPTED_CHALLENGE_SIZE)
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,13 +258,13 @@ void AuthSession::ReadHandler()
|
|||||||
if (!(*this.*itr->second.handler)())
|
if (!(*this.*itr->second.handler)())
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
packet.ReadCompleted(size);
|
packet.ReadCompleted(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncRead();
|
return SocketReadCallbackResult::KeepReading;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthSession::SendPacket(ByteBuffer& packet)
|
void AuthSession::SendPacket(ByteBuffer& packet)
|
||||||
|
|||||||
@@ -60,22 +60,22 @@ struct AccountInfo
|
|||||||
AccountTypes SecurityLevel = SEC_PLAYER;
|
AccountTypes SecurityLevel = SEC_PLAYER;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AuthSession : public Socket<AuthSession>
|
class AuthSession final : public Socket<AuthSession>
|
||||||
{
|
{
|
||||||
typedef Socket<AuthSession> AuthSocket;
|
typedef Socket<AuthSession> AuthSocket;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::unordered_map<uint8, AuthHandler> InitHandlers();
|
static std::unordered_map<uint8, AuthHandler> InitHandlers();
|
||||||
|
|
||||||
AuthSession(tcp::socket&& socket);
|
AuthSession(IoContextTcpSocket&& socket);
|
||||||
|
|
||||||
void Start() override;
|
void Start() override;
|
||||||
bool Update() override;
|
bool Update() final;
|
||||||
|
|
||||||
void SendPacket(ByteBuffer& packet);
|
void SendPacket(ByteBuffer& packet);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ReadHandler() override;
|
SocketReadCallbackResult ReadHandler() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool HandleLogonChallenge();
|
bool HandleLogonChallenge();
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ protected:
|
|||||||
return threads;
|
return threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
|
static void OnSocketAccept(IoContextTcpSocket&& sock, uint32 threadIndex)
|
||||||
{
|
{
|
||||||
Instance().OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
Instance().OnSocketOpen(std::move(sock), threadIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,18 +18,16 @@
|
|||||||
#ifndef __RASESSION_H__
|
#ifndef __RASESSION_H__
|
||||||
#define __RASESSION_H__
|
#define __RASESSION_H__
|
||||||
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include "Socket.h"
|
||||||
#include <boost/asio/streambuf.hpp>
|
#include <boost/asio/streambuf.hpp>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
|
||||||
using boost::asio::ip::tcp;
|
|
||||||
|
|
||||||
const std::size_t bufferSize = 4096;
|
const std::size_t bufferSize = 4096;
|
||||||
|
|
||||||
class RASession : public std::enable_shared_from_this<RASession>
|
class RASession : public std::enable_shared_from_this<RASession>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RASession(tcp::socket&& socket) :
|
RASession(IoContextTcpSocket&& socket) :
|
||||||
_socket(std::move(socket)), _commandExecuting(nullptr) { }
|
_socket(std::move(socket)), _commandExecuting(nullptr) { }
|
||||||
|
|
||||||
void Start();
|
void Start();
|
||||||
@@ -47,7 +45,7 @@ private:
|
|||||||
static void CommandPrint(void* callbackArg, std::string_view text);
|
static void CommandPrint(void* callbackArg, std::string_view text);
|
||||||
static void CommandFinished(void* callbackArg, bool);
|
static void CommandFinished(void* callbackArg, bool);
|
||||||
|
|
||||||
tcp::socket _socket;
|
IoContextTcpSocket _socket;
|
||||||
boost::asio::streambuf _readBuffer;
|
boost::asio::streambuf _readBuffer;
|
||||||
boost::asio::streambuf _writeBuffer;
|
boost::asio::streambuf _writeBuffer;
|
||||||
std::promise<void>* _commandExecuting;
|
std::promise<void>* _commandExecuting;
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ void ScriptMgr::OnNetworkStop()
|
|||||||
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_NETWORK_STOP, script->OnNetworkStop());
|
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_NETWORK_STOP, script->OnNetworkStop());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptMgr::OnSocketOpen(std::shared_ptr<WorldSocket> socket)
|
void ScriptMgr::OnSocketOpen(std::shared_ptr<WorldSocket> const& socket)
|
||||||
{
|
{
|
||||||
ASSERT(socket);
|
ASSERT(socket);
|
||||||
|
|
||||||
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_SOCKET_OPEN, script->OnSocketOpen(socket));
|
CALL_ENABLED_HOOKS(ServerScript, SERVERHOOK_ON_SOCKET_OPEN, script->OnSocketOpen(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptMgr::OnSocketClose(std::shared_ptr<WorldSocket> socket)
|
void ScriptMgr::OnSocketClose(std::shared_ptr<WorldSocket> const& socket)
|
||||||
{
|
{
|
||||||
ASSERT(socket);
|
ASSERT(socket);
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ public:
|
|||||||
virtual void OnNetworkStop() { }
|
virtual void OnNetworkStop() { }
|
||||||
|
|
||||||
// Called when a remote socket establishes a connection to the server. Do not store the socket object.
|
// Called when a remote socket establishes a connection to the server. Do not store the socket object.
|
||||||
virtual void OnSocketOpen(std::shared_ptr<WorldSocket> /*socket*/) { }
|
virtual void OnSocketOpen(std::shared_ptr<WorldSocket> const& /*socket*/) { }
|
||||||
|
|
||||||
// Called when a socket is closed. Do not store the socket object, and do not rely on the connection
|
// Called when a socket is closed. Do not store the socket object, and do not rely on the connection
|
||||||
// being open; it is not.
|
// being open; it is not.
|
||||||
virtual void OnSocketClose(std::shared_ptr<WorldSocket> /*socket*/) { }
|
virtual void OnSocketClose(std::shared_ptr<WorldSocket> const& /*socket*/) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This hook called when a packet is sent to a client. The packet object is a copy of the original packet, so reading and modifying it is safe.
|
* @brief This hook called when a packet is sent to a client. The packet object is a copy of the original packet, so reading and modifying it is safe.
|
||||||
|
|||||||
@@ -155,8 +155,8 @@ public: /* SpellScriptLoader */
|
|||||||
public: /* ServerScript */
|
public: /* ServerScript */
|
||||||
void OnNetworkStart();
|
void OnNetworkStart();
|
||||||
void OnNetworkStop();
|
void OnNetworkStop();
|
||||||
void OnSocketOpen(std::shared_ptr<WorldSocket> socket);
|
void OnSocketOpen(std::shared_ptr<WorldSocket> const& socket);
|
||||||
void OnSocketClose(std::shared_ptr<WorldSocket> socket);
|
void OnSocketClose(std::shared_ptr<WorldSocket> const& socket);
|
||||||
bool CanPacketReceive(WorldSession* session, WorldPacket const& packet);
|
bool CanPacketReceive(WorldSession* session, WorldPacket const& packet);
|
||||||
bool CanPacketSend(WorldSession* session, WorldPacket const& packet);
|
bool CanPacketSend(WorldSession* session, WorldPacket const& packet);
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ void EncryptableAndCompressiblePacket::CompressIfNeeded()
|
|||||||
SetOpcode(SMSG_COMPRESSED_UPDATE_OBJECT);
|
SetOpcode(SMSG_COMPRESSED_UPDATE_OBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldSocket::WorldSocket(tcp::socket&& socket)
|
WorldSocket::WorldSocket(IoContextTcpSocket&& socket)
|
||||||
: Socket(std::move(socket)), _OverSpeedPings(0), _worldSession(nullptr), _authed(false), _sendBufferSize(4096), _loggingPackets(false)
|
: Socket(std::move(socket)), _OverSpeedPings(0), _worldSession(nullptr), _authed(false), _sendBufferSize(4096), _loggingPackets(false)
|
||||||
{
|
{
|
||||||
Acore::Crypto::GetRandomBytes(_authSeed);
|
Acore::Crypto::GetRandomBytes(_authSeed);
|
||||||
@@ -238,10 +238,10 @@ void WorldSocket::OnClose()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocket::ReadHandler()
|
SocketReadCallbackResult WorldSocket::ReadHandler()
|
||||||
{
|
{
|
||||||
if (!IsOpen())
|
if (!IsOpen())
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
|
|
||||||
MessageBuffer& packet = GetReadBuffer();
|
MessageBuffer& packet = GetReadBuffer();
|
||||||
while (packet.GetActiveSize() > 0)
|
while (packet.GetActiveSize() > 0)
|
||||||
@@ -264,7 +264,7 @@ void WorldSocket::ReadHandler()
|
|||||||
if (!ReadHeaderHandler())
|
if (!ReadHeaderHandler())
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,11 +295,11 @@ void WorldSocket::ReadHandler()
|
|||||||
CloseSocket();
|
CloseSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return SocketReadCallbackResult::Stop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncRead();
|
return SocketReadCallbackResult::KeepReading;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WorldSocket::ReadHeaderHandler()
|
bool WorldSocket::ReadHeaderHandler()
|
||||||
|
|||||||
@@ -67,19 +67,19 @@ struct ClientPktHeader
|
|||||||
|
|
||||||
struct AuthSession;
|
struct AuthSession;
|
||||||
|
|
||||||
class AC_GAME_API WorldSocket : public Socket<WorldSocket>
|
class AC_GAME_API WorldSocket final : public Socket<WorldSocket>
|
||||||
{
|
{
|
||||||
typedef Socket<WorldSocket> BaseSocket;
|
typedef Socket<WorldSocket> BaseSocket;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WorldSocket(tcp::socket&& socket);
|
WorldSocket(IoContextTcpSocket&& socket);
|
||||||
~WorldSocket();
|
~WorldSocket();
|
||||||
|
|
||||||
WorldSocket(WorldSocket const& right) = delete;
|
WorldSocket(WorldSocket const& right) = delete;
|
||||||
WorldSocket& operator=(WorldSocket const& right) = delete;
|
WorldSocket& operator=(WorldSocket const& right) = delete;
|
||||||
|
|
||||||
void Start() override;
|
void Start() override;
|
||||||
bool Update() override;
|
bool Update() final;
|
||||||
|
|
||||||
void SendPacket(WorldPacket const& packet);
|
void SendPacket(WorldPacket const& packet);
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnClose() override;
|
void OnClose() override;
|
||||||
void ReadHandler() override;
|
SocketReadCallbackResult ReadHandler() final;
|
||||||
bool ReadHeaderHandler();
|
bool ReadHeaderHandler();
|
||||||
|
|
||||||
enum class ReadDataHandlerResult
|
enum class ReadDataHandlerResult
|
||||||
|
|||||||
@@ -25,13 +25,13 @@
|
|||||||
class WorldSocketThread : public NetworkThread<WorldSocket>
|
class WorldSocketThread : public NetworkThread<WorldSocket>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void SocketAdded(std::shared_ptr<WorldSocket> sock) override
|
void SocketAdded(std::shared_ptr<WorldSocket> const& sock) override
|
||||||
{
|
{
|
||||||
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
|
sock->SetSendBufferSize(sWorldSocketMgr.GetApplicationSendBufferSize());
|
||||||
sScriptMgr->OnSocketOpen(sock);
|
sScriptMgr->OnSocketOpen(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketRemoved(std::shared_ptr<WorldSocket> sock) override
|
void SocketRemoved(std::shared_ptr<WorldSocket> const& sock) override
|
||||||
{
|
{
|
||||||
sScriptMgr->OnSocketClose(sock);
|
sScriptMgr->OnSocketClose(sock);
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ void WorldSocketMgr::StopNetwork()
|
|||||||
sScriptMgr->OnNetworkStop();
|
sScriptMgr->OnNetworkStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
|
void WorldSocketMgr::OnSocketOpen(IoContextTcpSocket&& sock, uint32 threadIndex)
|
||||||
{
|
{
|
||||||
// set some options here
|
// set some options here
|
||||||
if (_socketSystemSendBufferSize >= 0)
|
if (_socketSystemSendBufferSize >= 0)
|
||||||
@@ -109,7 +109,7 @@ void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseSocketMgr::OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
BaseSocketMgr::OnSocketOpen(std::move(sock), threadIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const
|
NetworkThread<WorldSocket>* WorldSocketMgr::CreateThreads() const
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public:
|
|||||||
/// Stops all network threads, It will wait for all running threads .
|
/// Stops all network threads, It will wait for all running threads .
|
||||||
void StopNetwork() override;
|
void StopNetwork() override;
|
||||||
|
|
||||||
void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override;
|
void OnSocketOpen(IoContextTcpSocket&& sock, uint32 threadIndex) override;
|
||||||
|
|
||||||
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
|
std::size_t GetApplicationSendBufferSize() const { return _socketApplicationSendBufferSize; }
|
||||||
|
|
||||||
@@ -50,9 +50,9 @@ protected:
|
|||||||
|
|
||||||
NetworkThread<WorldSocket>* CreateThreads() const override;
|
NetworkThread<WorldSocket>* CreateThreads() const override;
|
||||||
|
|
||||||
static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex)
|
static void OnSocketAccept(IoContextTcpSocket&& sock, uint32 threadIndex)
|
||||||
{
|
{
|
||||||
Instance().OnSocketOpen(std::forward<tcp::socket>(sock), threadIndex);
|
Instance().OnSocketOpen(std::move(sock), threadIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "IpAddress.h"
|
#include "IpAddress.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "Socket.h"
|
||||||
#include "Systemd.h"
|
#include "Systemd.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
@@ -32,7 +33,7 @@ constexpr auto ACORE_MAX_LISTEN_CONNECTIONS = boost::asio::socket_base::max_list
|
|||||||
class AsyncAcceptor
|
class AsyncAcceptor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex);
|
typedef void(*AcceptCallback)(IoContextTcpSocket&& newSocket, uint32 threadIndex);
|
||||||
|
|
||||||
AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, bool supportSocketActivation = false) :
|
AsyncAcceptor(Acore::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port, bool supportSocketActivation = false) :
|
||||||
_acceptor(ioContext), _endpoint(Acore::Net::make_address(bindIp), port),
|
_acceptor(ioContext), _endpoint(Acore::Net::make_address(bindIp), port),
|
||||||
@@ -56,7 +57,7 @@ public:
|
|||||||
template<AcceptCallback acceptCallback>
|
template<AcceptCallback acceptCallback>
|
||||||
void AsyncAcceptWithCallback()
|
void AsyncAcceptWithCallback()
|
||||||
{
|
{
|
||||||
tcp::socket* socket;
|
IoContextTcpSocket* socket;
|
||||||
uint32 threadIndex;
|
uint32 threadIndex;
|
||||||
std::tie(socket, threadIndex) = _socketFactory();
|
std::tie(socket, threadIndex) = _socketFactory();
|
||||||
_acceptor.async_accept(*socket, [this, socket, threadIndex](boost::system::error_code error)
|
_acceptor.async_accept(*socket, [this, socket, threadIndex](boost::system::error_code error)
|
||||||
@@ -129,16 +130,16 @@ public:
|
|||||||
_acceptor.close(err);
|
_acceptor.close(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetSocketFactory(std::function<std::pair<tcp::socket*, uint32>()> func) { _socketFactory = func; }
|
void SetSocketFactory(std::function<std::pair<IoContextTcpSocket*, uint32>()> func) { _socketFactory = std::move(func); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::pair<tcp::socket*, uint32> DefaultSocketFactory() { return std::make_pair(&_socket, 0); }
|
std::pair<IoContextTcpSocket*, uint32> DefaultSocketFactory() { return std::make_pair(&_socket, 0); }
|
||||||
|
|
||||||
tcp::acceptor _acceptor;
|
boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, IoContextTcpSocket::executor_type> _acceptor;
|
||||||
tcp::endpoint _endpoint;
|
boost::asio::ip::tcp::endpoint _endpoint;
|
||||||
tcp::socket _socket;
|
IoContextTcpSocket _socket;
|
||||||
std::atomic<bool> _closed;
|
std::atomic<bool> _closed;
|
||||||
std::function<std::pair<tcp::socket*, uint32>()> _socketFactory;
|
std::function<std::pair<IoContextTcpSocket*, uint32>()> _socketFactory;
|
||||||
bool _supportSocketActivation;
|
bool _supportSocketActivation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -91,13 +91,13 @@ public:
|
|||||||
SocketAdded(sock);
|
SocketAdded(sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp::socket* GetSocketForAccept() { return &_acceptSocket; }
|
IoContextTcpSocket* GetSocketForAccept() { return &_acceptSocket; }
|
||||||
|
|
||||||
void EnableProxyProtocol() { _proxyHeaderReadingEnabled = true; }
|
void EnableProxyProtocol() { _proxyHeaderReadingEnabled = true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void SocketAdded(std::shared_ptr<SocketType> /*sock*/) { }
|
virtual void SocketAdded(std::shared_ptr<SocketType> const& /*sock*/) { }
|
||||||
virtual void SocketRemoved(std::shared_ptr<SocketType> /*sock*/) { }
|
virtual void SocketRemoved(std::shared_ptr<SocketType> const& /*sock*/) { }
|
||||||
|
|
||||||
void AddNewSockets()
|
void AddNewSockets()
|
||||||
{
|
{
|
||||||
@@ -229,7 +229,7 @@ private:
|
|||||||
SocketContainer _newSockets;
|
SocketContainer _newSockets;
|
||||||
|
|
||||||
Acore::Asio::IoContext _ioContext;
|
Acore::Asio::IoContext _ioContext;
|
||||||
tcp::socket _acceptSocket;
|
IoContextTcpSocket _acceptSocket;
|
||||||
boost::asio::steady_timer _updateTimer;
|
boost::asio::steady_timer _updateTimer;
|
||||||
|
|
||||||
bool _proxyHeaderReadingEnabled;
|
bool _proxyHeaderReadingEnabled;
|
||||||
|
|||||||
@@ -21,9 +21,8 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "MessageBuffer.h"
|
#include "MessageBuffer.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio/io_context.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -35,6 +34,23 @@ using boost::asio::ip::tcp;
|
|||||||
#define AC_SOCKET_USE_IOCP
|
#define AC_SOCKET_USE_IOCP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Specialize boost socket for io_context executor instead of type-erased any_io_executor
|
||||||
|
// This avoids the type-erasure overhead of any_io_executor
|
||||||
|
using IoContextTcpSocket = boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::io_context::executor_type>;
|
||||||
|
|
||||||
|
enum class SocketReadCallbackResult
|
||||||
|
{
|
||||||
|
KeepReading,
|
||||||
|
Stop
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SocketState : uint8
|
||||||
|
{
|
||||||
|
Open = 0,
|
||||||
|
Closing = 1,
|
||||||
|
Closed = 2
|
||||||
|
};
|
||||||
|
|
||||||
enum ProxyHeaderReadingState {
|
enum ProxyHeaderReadingState {
|
||||||
PROXY_HEADER_READING_STATE_NOT_STARTED,
|
PROXY_HEADER_READING_STATE_NOT_STARTED,
|
||||||
PROXY_HEADER_READING_STATE_STARTED,
|
PROXY_HEADER_READING_STATE_STARTED,
|
||||||
@@ -51,8 +67,8 @@ template<class T>
|
|||||||
class Socket : public std::enable_shared_from_this<T>
|
class Socket : public std::enable_shared_from_this<T>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Socket(tcp::socket&& socket) : _socket(std::move(socket)), _remoteAddress(_socket.remote_endpoint().address()),
|
explicit Socket(IoContextTcpSocket&& socket) : _socket(std::move(socket)), _remoteAddress(_socket.remote_endpoint().address()),
|
||||||
_remotePort(_socket.remote_endpoint().port()), _readBuffer(), _closed(false), _closing(false), _isWritingAsync(false),
|
_remotePort(_socket.remote_endpoint().port()), _readBuffer(), _state(SocketState::Open), _isWritingAsync(false),
|
||||||
_proxyHeaderReadingState(PROXY_HEADER_READING_STATE_NOT_STARTED)
|
_proxyHeaderReadingState(PROXY_HEADER_READING_STATE_NOT_STARTED)
|
||||||
{
|
{
|
||||||
_readBuffer.Resize(READ_BLOCK_SIZE);
|
_readBuffer.Resize(READ_BLOCK_SIZE);
|
||||||
@@ -60,7 +76,7 @@ public:
|
|||||||
|
|
||||||
virtual ~Socket()
|
virtual ~Socket()
|
||||||
{
|
{
|
||||||
_closed = true;
|
_state = SocketState::Closed;
|
||||||
boost::system::error_code error;
|
boost::system::error_code error;
|
||||||
_socket.close(error);
|
_socket.close(error);
|
||||||
}
|
}
|
||||||
@@ -69,13 +85,14 @@ public:
|
|||||||
|
|
||||||
virtual bool Update()
|
virtual bool Update()
|
||||||
{
|
{
|
||||||
if (_closed)
|
SocketState state = _state.load();
|
||||||
|
if (state == SocketState::Closed)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef AC_SOCKET_USE_IOCP
|
#ifndef AC_SOCKET_USE_IOCP
|
||||||
if (_isWritingAsync || (_writeQueue.empty() && !_closing))
|
if (_isWritingAsync || (_writeQueue.empty() && state != SocketState::Closing))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -150,12 +167,18 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] ProxyHeaderReadingState GetProxyHeaderReadingState() const { return _proxyHeaderReadingState; }
|
[[nodiscard]] ProxyHeaderReadingState GetProxyHeaderReadingState() const { return _proxyHeaderReadingState; }
|
||||||
|
|
||||||
[[nodiscard]] bool IsOpen() const { return !_closed && !_closing; }
|
[[nodiscard]] bool IsOpen() const { return _state.load() == SocketState::Open; }
|
||||||
|
|
||||||
void CloseSocket()
|
void CloseSocket()
|
||||||
{
|
{
|
||||||
if (_closed.exchange(true))
|
SocketState expected = SocketState::Open;
|
||||||
return;
|
if (!_state.compare_exchange_strong(expected, SocketState::Closed))
|
||||||
|
{
|
||||||
|
// If it was Closing, try to transition to Closed
|
||||||
|
expected = SocketState::Closing;
|
||||||
|
if (!_state.compare_exchange_strong(expected, SocketState::Closed))
|
||||||
|
return; // Already closed
|
||||||
|
}
|
||||||
|
|
||||||
boost::system::error_code shutdownError;
|
boost::system::error_code shutdownError;
|
||||||
_socket.shutdown(boost::asio::socket_base::shutdown_send, shutdownError);
|
_socket.shutdown(boost::asio::socket_base::shutdown_send, shutdownError);
|
||||||
@@ -168,13 +191,17 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the socket for closing after write buffer becomes empty
|
/// Marks the socket for closing after write buffer becomes empty
|
||||||
void DelayedCloseSocket() { _closing = true; }
|
void DelayedCloseSocket()
|
||||||
|
{
|
||||||
|
SocketState expected = SocketState::Open;
|
||||||
|
_state.compare_exchange_strong(expected, SocketState::Closing);
|
||||||
|
}
|
||||||
|
|
||||||
MessageBuffer& GetReadBuffer() { return _readBuffer; }
|
MessageBuffer& GetReadBuffer() { return _readBuffer; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnClose() { }
|
virtual void OnClose() { }
|
||||||
virtual void ReadHandler() = 0;
|
virtual SocketReadCallbackResult ReadHandler() = 0;
|
||||||
|
|
||||||
bool AsyncProcessQueue()
|
bool AsyncProcessQueue()
|
||||||
{
|
{
|
||||||
@@ -188,7 +215,7 @@ protected:
|
|||||||
_socket.async_write_some(boost::asio::buffer(buffer.GetReadPointer(), buffer.GetActiveSize()), std::bind(&Socket<T>::WriteHandler,
|
_socket.async_write_some(boost::asio::buffer(buffer.GetReadPointer(), buffer.GetActiveSize()), std::bind(&Socket<T>::WriteHandler,
|
||||||
this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
this->shared_from_this(), std::placeholders::_1, std::placeholders::_2));
|
||||||
#else
|
#else
|
||||||
_socket.async_wait(tcp::socket::wait_write, [self = this->shared_from_this()](boost::system::error_code error)
|
_socket.async_wait(boost::asio::socket_base::wait_write, [self = this->shared_from_this()](boost::system::error_code error)
|
||||||
{
|
{
|
||||||
self->WriteHandlerWrapper(error, 0);
|
self->WriteHandlerWrapper(error, 0);
|
||||||
});
|
});
|
||||||
@@ -216,7 +243,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
_readBuffer.WriteCompleted(transferredBytes);
|
_readBuffer.WriteCompleted(transferredBytes);
|
||||||
ReadHandler();
|
if (ReadHandler() == SocketReadCallbackResult::KeepReading)
|
||||||
|
AsyncRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyReadHeaderHandler reads Proxy Protocol v2 header (v1 is not supported).
|
// ProxyReadHeaderHandler reads Proxy Protocol v2 header (v1 is not supported).
|
||||||
@@ -344,7 +372,7 @@ private:
|
|||||||
|
|
||||||
if (!_writeQueue.empty())
|
if (!_writeQueue.empty())
|
||||||
AsyncProcessQueue();
|
AsyncProcessQueue();
|
||||||
else if (_closing)
|
else if (_state.load() == SocketState::Closing)
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -380,7 +408,7 @@ private:
|
|||||||
|
|
||||||
_writeQueue.pop();
|
_writeQueue.pop();
|
||||||
|
|
||||||
if (_closing && _writeQueue.empty())
|
if (_state.load() == SocketState::Closing && _writeQueue.empty())
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
}
|
}
|
||||||
@@ -391,7 +419,7 @@ private:
|
|||||||
{
|
{
|
||||||
_writeQueue.pop();
|
_writeQueue.pop();
|
||||||
|
|
||||||
if (_closing && _writeQueue.empty())
|
if (_state.load() == SocketState::Closing && _writeQueue.empty())
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
}
|
}
|
||||||
@@ -406,7 +434,7 @@ private:
|
|||||||
|
|
||||||
_writeQueue.pop();
|
_writeQueue.pop();
|
||||||
|
|
||||||
if (_closing && _writeQueue.empty())
|
if (_state.load() == SocketState::Closing && _writeQueue.empty())
|
||||||
{
|
{
|
||||||
CloseSocket();
|
CloseSocket();
|
||||||
}
|
}
|
||||||
@@ -415,7 +443,7 @@ private:
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tcp::socket _socket;
|
IoContextTcpSocket _socket;
|
||||||
|
|
||||||
boost::asio::ip::address _remoteAddress;
|
boost::asio::ip::address _remoteAddress;
|
||||||
uint16 _remotePort;
|
uint16 _remotePort;
|
||||||
@@ -423,8 +451,7 @@ private:
|
|||||||
MessageBuffer _readBuffer;
|
MessageBuffer _readBuffer;
|
||||||
std::queue<MessageBuffer> _writeQueue;
|
std::queue<MessageBuffer> _writeQueue;
|
||||||
|
|
||||||
std::atomic<bool> _closed;
|
std::atomic<SocketState> _state;
|
||||||
std::atomic<bool> _closing;
|
|
||||||
|
|
||||||
bool _isWritingAsync;
|
bool _isWritingAsync;
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public:
|
|||||||
_threads[i].Wait();
|
_threads[i].Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex)
|
virtual void OnSocketOpen(IoContextTcpSocket&& sock, uint32 threadIndex)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -117,7 +117,7 @@ public:
|
|||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<tcp::socket*, uint32> GetSocketForAccept()
|
std::pair<IoContextTcpSocket*, uint32> GetSocketForAccept()
|
||||||
{
|
{
|
||||||
uint32 threadIndex = SelectThreadWithMinConnections();
|
uint32 threadIndex = SelectThreadWithMinConnections();
|
||||||
return { _threads[threadIndex].GetSocketForAccept(), threadIndex };
|
return { _threads[threadIndex].GetSocketForAccept(), threadIndex };
|
||||||
|
|||||||
141
tools/socket_stress_heavy.py
Normal file
141
tools/socket_stress_heavy.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Socket Stress Test for AzerothCore
|
||||||
|
Tests authserver and worldserver connection handling under heavy load.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 socket_stress_heavy.py [duration_seconds] [auth_threads] [world_threads]
|
||||||
|
|
||||||
|
Defaults:
|
||||||
|
duration: 300 seconds (5 minutes)
|
||||||
|
auth_threads: 100
|
||||||
|
world_threads: 150
|
||||||
|
"""
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import sys
|
||||||
|
|
||||||
|
AUTH_PORT = 3724
|
||||||
|
WORLD_PORT = 8085
|
||||||
|
HOST = '127.0.0.1'
|
||||||
|
|
||||||
|
stats = {'auth_ok': 0, 'auth_fail': 0, 'world_ok': 0, 'world_fail': 0}
|
||||||
|
running = True
|
||||||
|
|
||||||
|
|
||||||
|
def stress_auth():
|
||||||
|
"""Flood authserver with login challenge packets."""
|
||||||
|
global stats
|
||||||
|
while running:
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(1)
|
||||||
|
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
s.connect((HOST, AUTH_PORT))
|
||||||
|
# AUTH_LOGON_CHALLENGE packet
|
||||||
|
packet = bytes([
|
||||||
|
0x00, # cmd: AUTH_LOGON_CHALLENGE
|
||||||
|
0x00, # error
|
||||||
|
0x24, 0x00, # size (36)
|
||||||
|
0x57, 0x6F, 0x57, 0x00, # 'WoW\0'
|
||||||
|
0x03, 0x03, 0x05, # version 3.3.5
|
||||||
|
0x30, 0x30, # build 12340
|
||||||
|
0x78, 0x38, 0x36, 0x00, # 'x86\0'
|
||||||
|
0x6E, 0x69, 0x57, 0x00, # 'niW\0' (Win reversed)
|
||||||
|
0x53, 0x55, 0x6E, 0x65, # 'SUne' (enUS reversed)
|
||||||
|
0x3C, 0x00, 0x00, 0x00, # timezone
|
||||||
|
0x7F, 0x00, 0x00, 0x01, # IP 127.0.0.1
|
||||||
|
0x04, # account name length
|
||||||
|
0x54, 0x45, 0x53, 0x54 # 'TEST'
|
||||||
|
])
|
||||||
|
s.sendall(packet)
|
||||||
|
s.close()
|
||||||
|
stats['auth_ok'] += 1
|
||||||
|
except Exception:
|
||||||
|
stats['auth_fail'] += 1
|
||||||
|
|
||||||
|
|
||||||
|
def stress_world():
|
||||||
|
"""Flood worldserver with connection attempts."""
|
||||||
|
global stats
|
||||||
|
while running:
|
||||||
|
try:
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(1)
|
||||||
|
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
|
s.connect((HOST, WORLD_PORT))
|
||||||
|
# Wait for SMSG_AUTH_CHALLENGE
|
||||||
|
s.recv(64)
|
||||||
|
s.close()
|
||||||
|
stats['world_ok'] += 1
|
||||||
|
except Exception:
|
||||||
|
stats['world_fail'] += 1
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global running
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
duration = int(sys.argv[1]) if len(sys.argv) > 1 else 300
|
||||||
|
auth_threads = int(sys.argv[2]) if len(sys.argv) > 2 else 100
|
||||||
|
world_threads = int(sys.argv[3]) if len(sys.argv) > 3 else 150
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print("SOCKET STRESS TEST")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Duration: {duration} seconds")
|
||||||
|
print(f"Auth threads: {auth_threads} -> {HOST}:{AUTH_PORT}")
|
||||||
|
print(f"World threads: {world_threads} -> {HOST}:{WORLD_PORT}")
|
||||||
|
print("-" * 60)
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
|
||||||
|
for _ in range(auth_threads):
|
||||||
|
t = threading.Thread(target=stress_auth, daemon=True)
|
||||||
|
t.start()
|
||||||
|
threads.append(t)
|
||||||
|
|
||||||
|
for _ in range(world_threads):
|
||||||
|
t = threading.Thread(target=stress_world, daemon=True)
|
||||||
|
t.start()
|
||||||
|
threads.append(t)
|
||||||
|
|
||||||
|
print(f"Started {len(threads)} threads")
|
||||||
|
print("-" * 60)
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
while time.time() - start < duration:
|
||||||
|
elapsed = int(time.time() - start)
|
||||||
|
total = stats['auth_ok'] + stats['world_ok']
|
||||||
|
rate = total / max(elapsed, 1)
|
||||||
|
print(f"\r[{elapsed:3d}s] Auth: {stats['auth_ok']:7d} ok {stats['auth_fail']:5d} fail | "
|
||||||
|
f"World: {stats['world_ok']:7d} ok {stats['world_fail']:5d} fail | "
|
||||||
|
f"Rate: {rate:,.0f}/s ", end='', flush=True)
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n\nInterrupted by user")
|
||||||
|
|
||||||
|
running = False
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
total_ok = stats['auth_ok'] + stats['world_ok']
|
||||||
|
total_fail = stats['auth_fail'] + stats['world_fail']
|
||||||
|
elapsed = time.time() - start
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("RESULTS:")
|
||||||
|
print(f" Duration: {elapsed:.1f} seconds")
|
||||||
|
print(f" Auth: {stats['auth_ok']:,} ok, {stats['auth_fail']:,} failed")
|
||||||
|
print(f" World: {stats['world_ok']:,} ok, {stats['world_fail']:,} failed")
|
||||||
|
print(f" Total: {total_ok:,} ok, {total_fail:,} failed")
|
||||||
|
print(f" Rate: {total_ok / elapsed:,.0f} connections/sec average")
|
||||||
|
if total_fail > 0:
|
||||||
|
print(f" Failure: {total_fail / (total_ok + total_fail) * 100:.2f}%")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user