18#ifndef TRINITYCORE_SOCKET_H
19#define TRINITYCORE_SOCKET_H
26#include <boost/asio/compose.hpp>
27#include <boost/asio/io_context.hpp>
28#include <boost/asio/ip/tcp.hpp>
34#ifdef BOOST_ASIO_HAS_IOCP
35#define TC_SOCKET_USE_IOCP
40using IoContextTcpSocket = boost::asio::basic_stream_socket<boost::asio::ip::tcp, boost::asio::io_context::executor_type>;
42namespace Impl::Operations
44template <
typename Socket>
61template <
typename SocketType>
72template <
typename AsyncReadObjectType,
typename ReadHandlerObjectType = AsyncReadObjectType>
125template<
class Stream = IoContextTcpSocket>
126class Socket :
public std::enable_shared_from_this<Socket<Stream>>
129 template<
typename... Args>
135 template<
typename... Args>
148 boost::system::error_code error;
154 template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(boost::system::error_code, boost::asio::ip::tcp::endpo
int)) Callback>
155 decltype(
auto)
Connect(boost::asio::ip::tcp::endpoint
const& endpoint, Callback&& callback)
162 template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(boost::system::error_code, boost::asio::ip::tcp::endpo
int)) Callback>
163 decltype(
auto)
Connect(std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints, Callback&& callback)
175#ifndef TC_SOCKET_USE_IOCP
201 template <invocable_r<SocketReadCallbackResult> Callback>
208 [self = this->shared_from_this(), callback = std::forward<Callback>(callback)](boost::system::error_code
const& error,
size_t transferredBytes)
mutable
210 if (self->ReadHandlerInternal(error, transferredBytes))
212 self->AsyncRead(std::forward<Callback>(callback));
220#ifdef TC_SOCKET_USE_IOCP
232 boost::system::error_code shutdownError;
233 _socket.shutdown(boost::asio::socket_base::shutdown_send, shutdownError);
236 shutdownError.value(), shutdownError.message());
271#ifdef TC_SOCKET_USE_IOCP
274 [self = this->shared_from_this()](boost::system::error_code
const& error, std::size_t transferedBytes)
276 self->WriteHandler(error, transferedBytes);
279 _socket.async_wait(boost::asio::socket_base::wait_type::wait_write,
280 [self = this->shared_from_this()](boost::system::error_code
const& error)
282 self->WriteHandlerWrapper(error);
291 boost::system::error_code err;
292 _socket.set_option(boost::asio::ip::tcp::no_delay(enable), err);
294 TC_LOG_DEBUG(
"network",
"Socket::SetNoDelay: failed to set_option(boost::asio::ip::tcp::no_delay) for {} - {} ({})",
318#ifdef TC_SOCKET_USE_IOCP
320 void WriteHandler(boost::system::error_code
const& error, std::size_t transferedBytes)
325 _writeQueue.front().ReadCompleted(transferedBytes);
353 boost::system::error_code error;
354 std::size_t bytesSent =
_socket.write_some(boost::asio::buffer(queuedMessage.
GetReadPointer(), bytesToSend), error);
358 if (error == boost::asio::error::would_block || error == boost::asio::error::try_again)
364 else if (bytesSent == 0)
369 else if (bytesSent < bytesToSend)
405namespace Impl::Operations
409 explicit ConnectState(std::shared_ptr<void>
const& socketRef, boost::asio::ip::tcp::endpoint
const& endpoint)
412 explicit ConnectState(std::shared_ptr<void>
const& socketRef, std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints)
420template <
typename Socket>
423 explicit Connect(std::shared_ptr<Socket>
const& socketRef, boost::asio::ip::tcp::endpoint
const& endpoint)
426 explicit Connect(std::shared_ptr<Socket>
const& socketRef, std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints)
429 std::shared_ptr<ConnectState>
State;
431 template <
typename Handler>
432 void operator()(Handler& handler, boost::system::error_code error = {})
434 std::shared_ptr<Socket> socket = static_pointer_cast<Socket>(
State->SocketRef.lock());
437 error = boost::asio::error::operation_aborted;
438 handler.complete(error, boost::asio::ip::tcp::endpoint());
442 bool isFirst =
State->Index < 0;
444 if (std::max(
State->Index, std::ptrdiff_t(0)) >= std::ssize(
State->Endpoints))
447 error = boost::asio::error::not_found;
448 handler.complete(error, boost::asio::ip::tcp::endpoint());
452 if (!isFirst && !socket->underlying_stream().is_open())
455 error = boost::asio::error::operation_aborted;
456 handler.complete(error, boost::asio::ip::tcp::endpoint());
460 if (!error && !isFirst)
462 socket->SetRemoteEndpoint(
State->Endpoints[
State->Index]);
463 handler.complete(error,
State->Endpoints[
State->Index]);
467#if BOOST_VERSION >= 107700
468 if (handler.get_cancellation_state().cancelled() != boost::asio::cancellation_type::none)
471 error = boost::asio::error::operation_aborted;
472 handler.complete(error, boost::asio::ip::tcp::endpoint());
477 socket->underlying_stream().close(error);
478 socket->underlying_stream().async_connect(
State->Endpoints[++
State->Index], std::move(handler));
484 TC_LOG_DEBUG(
"network",
"Socket::Connect: {}", message);
#define TC_LOG_DEBUG(filterType__, message__,...)
size_type GetRemainingSpace() const
void ReadCompleted(size_type bytes)
void WriteCompleted(size_type bytes)
size_type GetActiveSize() const
uint8 * GetWritePointer()
uint16 GetRemotePort() const
decltype(auto) Connect(std::vector< boost::asio::ip::tcp::endpoint > const &endpoints, Callback &&callback)
static constexpr uint8 OpenState_Closed
std::atomic< uint8 > _openState
void QueuedBufferWriteDone()
Socket(Socket const &other)=delete
bool ReadHandlerInternal(boost::system::error_code const &error, size_t transferredBytes)
void SetNoDelay(bool enable)
Socket(IoContextTcpSocket &&socket, Args &&... args)
std::queue< MessageBuffer > _writeQueue
virtual SocketReadCallbackResult ReadHandler()
Socket & operator=(Socket const &other)=delete
Stream & underlying_stream()
boost::asio::ip::address const & GetRemoteIpAddress() const
static constexpr uint8 OpenState_Closing
Transition to Closed state after sending all queued data.
static constexpr uint8 OpenState_Open
Socket(boost::asio::io_context &context, Args &&... args)
void AsyncRead(Callback &&callback)
void QueuePacket(MessageBuffer &&buffer)
void DelayedCloseSocket()
Marks the socket for closing after write buffer becomes empty.
MessageBuffer _readBuffer
void WriteHandlerWrapper(boost::system::error_code const &)
decltype(auto) Connect(boost::asio::ip::tcp::endpoint const &endpoint, Callback &&callback)
void SetRemoteEndpoint(boost::asio::ip::tcp::endpoint const &endpoint)
Socket(Socket &&other)=delete
Socket & operator=(Socket &&other)=delete
struct Trinity::Net::Socket::Endpoint _remoteEndpoint
MessageBuffer & GetReadBuffer()
boost::asio::basic_stream_socket< boost::asio::ip::tcp, boost::asio::io_context::executor_type > IoContextTcpSocket
boost::asio::mutable_buffer PrepareReadBuffer(MessageBuffer &readBuffer)
std::weak_ptr< void > SocketRef
ConnectState(std::shared_ptr< void > const &socketRef, boost::asio::ip::tcp::endpoint const &endpoint)
ConnectState(std::shared_ptr< void > const &socketRef, std::vector< boost::asio::ip::tcp::endpoint > const &endpoints)
std::vector< boost::asio::ip::tcp::endpoint > Endpoints
void operator()(Handler &handler, boost::system::error_code error={})
std::shared_ptr< ConnectState > State
Connect(std::shared_ptr< Socket > const &socketRef, boost::asio::ip::tcp::endpoint const &endpoint)
static void HandleError(Socket *self, std::string_view message)
Connect(std::shared_ptr< Socket > const &socketRef, std::vector< boost::asio::ip::tcp::endpoint > const &endpoints)
SocketReadCallbackResult operator()() const
AsyncReadObjectType * Socket
InvokeReadHandlerCallback< ReadHandlerObjectType > ReadCallback
ReadConnectionInitializer(AsyncReadObjectType *socket, ReadHandlerObjectType *callbackSocket)
ReadConnectionInitializer(AsyncReadObjectType *socket)
boost::asio::ip::address Address