18#ifndef TRINITYCORE_SOCKET_H
19#define TRINITYCORE_SOCKET_H
27#include <boost/asio/compose.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, Asio::IoContextExecutor>;
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>
149 boost::system::error_code error;
155 template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(boost::system::error_code, boost::asio::ip::tcp::endpo
int)) Callback>
156 decltype(
auto)
Connect(boost::asio::ip::tcp::endpoint
const& endpoint, Callback&& callback)
163 template <BOOST_ASIO_COMPLETION_TOKEN_FOR(
void(boost::system::error_code, boost::asio::ip::tcp::endpo
int)) Callback>
164 decltype(
auto)
Connect(std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints, Callback&& callback)
176#ifndef TC_SOCKET_USE_IOCP
202 template <invocable_r<SocketReadCallbackResult> Callback>
209 [self = this->shared_from_this(), callback = std::forward<Callback>(callback)](boost::system::error_code
const& error,
size_t transferredBytes)
mutable
211 if (self->ReadHandlerInternal(error, transferredBytes))
213 self->AsyncRead(std::forward<Callback>(callback));
221#ifdef TC_SOCKET_USE_IOCP
233 boost::system::error_code shutdownError;
234 _socket.shutdown(boost::asio::socket_base::shutdown_send, shutdownError);
237 shutdownError.value(), shutdownError.message());
272#ifdef TC_SOCKET_USE_IOCP
275 [self = this->shared_from_this()](boost::system::error_code
const& error, std::size_t transferedBytes)
277 self->WriteHandler(error, transferedBytes);
280 _socket.async_wait(boost::asio::socket_base::wait_type::wait_write,
281 [self = this->shared_from_this()](boost::system::error_code
const& error)
283 self->WriteHandlerWrapper(error);
292 boost::system::error_code err;
293 _socket.set_option(boost::asio::ip::tcp::no_delay(enable), err);
295 TC_LOG_DEBUG(
"network",
"Socket::SetNoDelay: failed to set_option(boost::asio::ip::tcp::no_delay) for {} - {} ({})",
319#ifdef TC_SOCKET_USE_IOCP
321 void WriteHandler(boost::system::error_code
const& error, std::size_t transferedBytes)
326 _writeQueue.front().ReadCompleted(transferedBytes);
354 boost::system::error_code error;
355 std::size_t bytesSent =
_socket.write_some(boost::asio::buffer(queuedMessage.
GetReadPointer(), bytesToSend), error);
359 if (error == boost::asio::error::would_block || error == boost::asio::error::try_again)
365 else if (bytesSent == 0)
370 else if (bytesSent < bytesToSend)
406namespace Impl::Operations
410 explicit ConnectState(std::shared_ptr<void>
const& socketRef, boost::asio::ip::tcp::endpoint
const& endpoint)
413 explicit ConnectState(std::shared_ptr<void>
const& socketRef, std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints)
421template <
typename Socket>
424 explicit Connect(std::shared_ptr<Socket>
const& socketRef, boost::asio::ip::tcp::endpoint
const& endpoint)
427 explicit Connect(std::shared_ptr<Socket>
const& socketRef, std::vector<boost::asio::ip::tcp::endpoint>
const& endpoints)
430 std::shared_ptr<ConnectState>
State;
432 template <
typename Handler>
433 void operator()(Handler& handler, boost::system::error_code error = {})
435 std::shared_ptr<Socket> socket = static_pointer_cast<Socket>(
State->SocketRef.lock());
438 error = boost::asio::error::operation_aborted;
439 handler.complete(error, boost::asio::ip::tcp::endpoint());
443 bool isFirst =
State->Index < 0;
445 if (std::max(
State->Index, std::ptrdiff_t(0)) >= std::ssize(
State->Endpoints))
448 error = boost::asio::error::not_found;
449 handler.complete(error, boost::asio::ip::tcp::endpoint());
453 if (!isFirst && !socket->underlying_stream().is_open())
456 error = boost::asio::error::operation_aborted;
457 handler.complete(error, boost::asio::ip::tcp::endpoint());
461 if (!error && !isFirst)
463 socket->SetRemoteEndpoint(
State->Endpoints[
State->Index]);
464 handler.complete(error,
State->Endpoints[
State->Index]);
468#if BOOST_VERSION >= 107700
469 if (handler.get_cancellation_state().cancelled() != boost::asio::cancellation_type::none)
472 error = boost::asio::error::operation_aborted;
473 handler.complete(error, boost::asio::ip::tcp::endpoint());
478 socket->underlying_stream().close(error);
479 socket->underlying_stream().async_connect(
State->Endpoints[++
State->Index], std::move(handler));
485 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
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 &)
Socket(Asio::IoContext &context, Args &&... args)
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, Asio::IoContextExecutor > 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