TrinityCore
AsyncAcceptor.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef __ASYNCACCEPT_H_
19#define __ASYNCACCEPT_H_
20
21#include "IoContext.h"
22#include "IpAddress.h"
23#include "Log.h"
24#include <boost/asio/ip/tcp.hpp>
25#include <atomic>
26#include <functional>
27
28#define TRINITY_MAX_LISTEN_CONNECTIONS boost::asio::socket_base::max_listen_connections
29
31{
32public:
33 typedef void(*AcceptCallback)(boost::asio::ip::tcp::socket&& newSocket, uint32 threadIndex);
34
35 AsyncAcceptor(Trinity::Asio::IoContext& ioContext, std::string const& bindIp, uint16 port) :
36 _acceptor(ioContext), _endpoint(Trinity::Net::make_address(bindIp), port),
37 _socket(ioContext), _closed(false), _socketFactory([this] { return DefeaultSocketFactory(); })
38 {
39 }
40
41 template<class T>
42 void AsyncAccept();
43
44 template<AcceptCallback acceptCallback>
46 {
47 auto [tmpSocket, tmpThreadIndex] = _socketFactory();
48 // TODO: get rid of temporary variables (clang 15 cannot handle variables from structured bindings as lambda captures)
49 boost::asio::ip::tcp::socket* socket = tmpSocket;
50 uint32 threadIndex = tmpThreadIndex;
51 _acceptor.async_accept(*socket, [this, socket, threadIndex](boost::system::error_code error)
52 {
53 if (!error)
54 {
55 try
56 {
57 socket->non_blocking(true);
58
59 acceptCallback(std::move(*socket), threadIndex);
60 }
61 catch (boost::system::system_error const& err)
62 {
63 TC_LOG_INFO("network", "Failed to initialize client's socket {}", err.what());
64 }
65 }
66
67 if (!_closed)
68 this->AsyncAcceptWithCallback<acceptCallback>();
69 });
70 }
71
72 bool Bind()
73 {
74 boost::system::error_code errorCode;
75 _acceptor.open(_endpoint.protocol(), errorCode);
76 if (errorCode)
77 {
78 TC_LOG_INFO("network", "Failed to open acceptor {}", errorCode.message());
79 return false;
80 }
81
82#if TRINITY_PLATFORM != TRINITY_PLATFORM_WINDOWS
83 _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true), errorCode);
84 if (errorCode)
85 {
86 TC_LOG_INFO("network", "Failed to set reuse_address option on acceptor {}", errorCode.message());
87 return false;
88 }
89#endif
90
91 _acceptor.bind(_endpoint, errorCode);
92 if (errorCode)
93 {
94 TC_LOG_INFO("network", "Could not bind to {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message());
95 return false;
96 }
97
99 if (errorCode)
100 {
101 TC_LOG_INFO("network", "Failed to start listening on {}:{} {}", _endpoint.address().to_string(), _endpoint.port(), errorCode.message());
102 return false;
103 }
104
105 return true;
106 }
107
108 void Close()
109 {
110 if (_closed.exchange(true))
111 return;
112
113 boost::system::error_code err;
114 _acceptor.close(err);
115 }
116
117 void SetSocketFactory(std::function<std::pair<boost::asio::ip::tcp::socket*, uint32>()> func) { _socketFactory = std::move(func); }
118
119private:
120 std::pair<boost::asio::ip::tcp::socket*, uint32> DefeaultSocketFactory() { return std::make_pair(&_socket, 0); }
121
122 boost::asio::ip::tcp::acceptor _acceptor;
123 boost::asio::ip::tcp::endpoint _endpoint;
124 boost::asio::ip::tcp::socket _socket;
125 std::atomic<bool> _closed;
126 std::function<std::pair<boost::asio::ip::tcp::socket*, uint32>()> _socketFactory;
127};
128
129template<class T>
131{
132 _acceptor.async_accept(_socket, [this](boost::system::error_code error)
133 {
134 if (!error)
135 {
136 try
137 {
138 // this-> is required here to fix an segmentation fault in gcc 4.7.2 - reason is lambdas in a templated class
139 std::make_shared<T>(std::move(this->_socket))->Start();
140 }
141 catch (boost::system::system_error const& err)
142 {
143 TC_LOG_INFO("network", "Failed to retrieve client's remote address {}", err.what());
144 }
145 }
146
147 // lets slap some more this-> on this so we can fix this bug with gcc 4.7.2 throwing internals in yo face
148 if (!_closed)
149 this->AsyncAccept<T>();
150 });
151}
152
153#endif /* __ASYNCACCEPT_H_ */
#define TRINITY_MAX_LISTEN_CONNECTIONS
Definition: AsyncAcceptor.h:28
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
void AsyncAccept()
std::atomic< bool > _closed
boost::asio::ip::tcp::endpoint _endpoint
AsyncAcceptor(Trinity::Asio::IoContext &ioContext, std::string const &bindIp, uint16 port)
Definition: AsyncAcceptor.h:35
boost::asio::ip::tcp::socket _socket
void SetSocketFactory(std::function< std::pair< boost::asio::ip::tcp::socket *, uint32 >()> func)
boost::asio::ip::tcp::acceptor _acceptor
void(* AcceptCallback)(boost::asio::ip::tcp::socket &&newSocket, uint32 threadIndex)
Definition: AsyncAcceptor.h:33
std::pair< boost::asio::ip::tcp::socket *, uint32 > DefeaultSocketFactory()
void AsyncAcceptWithCallback()
Definition: AsyncAcceptor.h:45
std::function< std::pair< boost::asio::ip::tcp::socket *, uint32 >()> _socketFactory