10 #include <boost/asio.hpp>
11 #include <boost/bind.hpp>
12 #include <boost/thread/mutex.hpp>
13 #include <pion/PionAdminRights.hpp>
14 #include <pion/net/TCPServer.hpp>
16 using boost::asio::ip::tcp;
26 : m_logger(PION_GET_LOGGER(
"pion.net.TCPServer")),
27 m_active_scheduler(scheduler),
28 m_tcp_acceptor(m_active_scheduler.getIOService()),
30 m_ssl_context(m_active_scheduler.getIOService(),
boost::asio::ssl::context::sslv23),
34 m_endpoint(tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
38 : m_logger(PION_GET_LOGGER(
"pion.net.TCPServer")),
39 m_active_scheduler(scheduler),
40 m_tcp_acceptor(m_active_scheduler.getIOService()),
42 m_ssl_context(m_active_scheduler.getIOService(),
boost::asio::ssl::context::sslv23),
46 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
50 : m_logger(PION_GET_LOGGER(
"pion.net.TCPServer")),
51 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
52 m_tcp_acceptor(m_active_scheduler.getIOService()),
54 m_ssl_context(m_active_scheduler.getIOService(),
boost::asio::ssl::context::sslv23),
58 m_endpoint(tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
62 : m_logger(PION_GET_LOGGER(
"pion.net.TCPServer")),
63 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
64 m_tcp_acceptor(m_active_scheduler.getIOService()),
66 m_ssl_context(m_active_scheduler.getIOService(),
boost::asio::ssl::context::sslv23),
70 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
76 boost::mutex::scoped_lock server_lock(m_mutex);
78 if (! m_is_listening) {
87 m_tcp_acceptor.open(m_endpoint.protocol());
91 m_tcp_acceptor.set_option(tcp::acceptor::reuse_address(
true));
93 m_tcp_acceptor.bind(m_endpoint);
94 if (m_endpoint.port() == 0) {
96 m_endpoint = m_tcp_acceptor.local_endpoint();
98 m_tcp_acceptor.listen();
99 }
catch (std::exception& e) {
100 PION_LOG_ERROR(
m_logger,
"Unable to bind to port " <<
getPort() <<
": " << e.what());
104 m_is_listening =
true;
107 server_lock.unlock();
118 boost::mutex::scoped_lock server_lock(m_mutex);
120 if (m_is_listening) {
123 m_is_listening =
false;
126 m_tcp_acceptor.close();
128 if (! wait_until_finished) {
130 std::for_each(m_conn_pool.begin(), m_conn_pool.end(),
135 while (! m_conn_pool.empty()) {
137 if (pruneConnections() == 0)
140 PION_LOG_INFO(
m_logger,
"Waiting for open connections to finish");
149 m_server_has_stopped.notify_all();
155 boost::mutex::scoped_lock server_lock(m_mutex);
156 while (m_is_listening) {
158 m_server_has_stopped.wait(server_lock);
167 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds
168 | boost::asio::ssl::context::no_sslv2
169 | boost::asio::ssl::context::single_dh_use);
170 m_ssl_context.use_certificate_file(pem_key_file, boost::asio::ssl::context::pem);
171 m_ssl_context.use_private_key_file(pem_key_file, boost::asio::ssl::context::pem);
175 void TCPServer::listen(
void)
178 boost::mutex::scoped_lock server_lock(m_mutex);
180 if (m_is_listening) {
183 m_ssl_context, m_ssl_flag,
184 boost::bind(&TCPServer::finishConnection,
191 m_conn_pool.insert(new_connection);
194 new_connection->async_accept(m_tcp_acceptor,
195 boost::bind(&TCPServer::handleAccept,
196 this, new_connection,
197 boost::asio::placeholders::error));
201 void TCPServer::handleAccept(TCPConnectionPtr& tcp_conn,
202 const boost::system::error_code& accept_error)
207 if (m_is_listening) {
209 PION_LOG_WARN(
m_logger,
"Accept error on port " <<
getPort() <<
": " << accept_error.message());
211 finishConnection(tcp_conn);
214 PION_LOG_DEBUG(
m_logger,
"New" << (tcp_conn->getSSLFlag() ?
" SSL " :
" ")
215 <<
"connection on port " <<
getPort());
219 if (m_is_listening) listen();
223 if (tcp_conn->getSSLFlag()) {
224 tcp_conn->async_handshake_server(boost::bind(&TCPServer::handleSSLHandshake,
226 boost::asio::placeholders::error));
234 void TCPServer::handleSSLHandshake(TCPConnectionPtr& tcp_conn,
235 const boost::system::error_code& handshake_error)
237 if (handshake_error) {
240 <<
" (" << handshake_error.message() <<
')');
241 finishConnection(tcp_conn);
244 PION_LOG_DEBUG(
m_logger,
"SSL handshake succeeded on port " <<
getPort());
249 void TCPServer::finishConnection(TCPConnectionPtr& tcp_conn)
251 boost::mutex::scoped_lock server_lock(m_mutex);
252 if (m_is_listening && tcp_conn->getKeepAlive()) {
261 ConnectionPool::iterator conn_itr = m_conn_pool.find(tcp_conn);
262 if (conn_itr != m_conn_pool.end())
263 m_conn_pool.erase(conn_itr);
266 if (!m_is_listening && m_conn_pool.empty())
267 m_no_more_connections.notify_all();
271 std::size_t TCPServer::pruneConnections(
void)
274 ConnectionPool::iterator conn_itr = m_conn_pool.begin();
275 while (conn_itr != m_conn_pool.end()) {
276 if (conn_itr->unique()) {
277 PION_LOG_WARN(
m_logger,
"Closing orphaned connection on port " <<
getPort());
278 ConnectionPool::iterator erase_itr = conn_itr;
280 (*erase_itr)->close();
281 m_conn_pool.erase(erase_itr);
288 return m_conn_pool.size();
293 boost::mutex::scoped_lock server_lock(m_mutex);
294 return (m_is_listening ? (m_conn_pool.size() - 1) : m_conn_pool.size());
virtual void beforeStarting(void)
called before the TCP server starts listening for new connections
virtual void afterStopping(void)
called after the TCP server has stopped listing for new connections
void setSSLKeyFile(const std::string &pem_key_file)
static boost::shared_ptr< TCPConnection > create(boost::asio::io_service &io_service, SSLContext &ssl_context, const bool ssl_flag, ConnectionHandler finished_handler)
static void sleep(boost::uint32_t sleep_sec, boost::uint32_t sleep_nsec)
std::size_t getConnections(void) const
returns the number of active tcp connections
void join(void)
the calling thread will sleep until the server has stopped listening for connections ...
virtual void handleConnection(TCPConnectionPtr &tcp_conn)
void start(void)
starts listening for new connections
PionLogger m_logger
primary logging interface used by this class
TCPServer(const unsigned int tcp_port)
void removeActiveUser(void)
unregisters an active user with the thread scheduler
void close(void)
closes the tcp socket and cancels any pending asynchronous operations
unsigned int getPort(void) const
returns tcp port number that the server listens for connections on
the following enables use of the lock-free cache
void stop(bool wait_until_finished=false)
boost::asio::io_service & getIOService(void)
returns an async I/O service used to schedule work
void setSSLFlag(bool b=true)
sets value of SSL flag (true if the server uses SSL to encrypt connections)