source: finroc_plugins_tcp/internal/tServer.cpp @ 182:ae4f8f53110b

14.08
Last change on this file since 182:ae4f8f53110b was 182:ae4f8f53110b, checked in by Max Reichardt <max.reichardt@…>, 3 years ago

Improves and limits amount of command line output if TCP server socket cannot accept connections

File size: 7.5 KB
Line 
1//
2// You received this file as part of Finroc
3// A framework for intelligent robot control
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    plugins/tcp/internal/tServer.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2013-01-04
27 *
28 */
29//----------------------------------------------------------------------
30#include "plugins/tcp/internal/tServer.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include <boost/asio/ip/v6_only.hpp>
36#include "rrlib/thread/tThread.h"
37
38//----------------------------------------------------------------------
39// Internal includes with ""
40//----------------------------------------------------------------------
41#include "plugins/tcp/tSettings.h"
42#include "plugins/tcp/internal/tConnection.h"
43#include "plugins/tcp/internal/tPeerImplementation.h"
44#include "plugins/tcp/internal/util.h"
45
46//----------------------------------------------------------------------
47// Debugging
48//----------------------------------------------------------------------
49#include <cassert>
50
51//----------------------------------------------------------------------
52// Namespace usage
53//----------------------------------------------------------------------
54
55//----------------------------------------------------------------------
56// Namespace declaration
57//----------------------------------------------------------------------
58namespace finroc
59{
60namespace tcp
61{
62namespace internal
63{
64
65//----------------------------------------------------------------------
66// Forward declarations / typedefs / enums
67//----------------------------------------------------------------------
68
69//----------------------------------------------------------------------
70// Const values
71//----------------------------------------------------------------------
72
73//----------------------------------------------------------------------
74// Implementation
75//----------------------------------------------------------------------
76
77namespace
78{
79
80size_t connection_error_count = 0;
81rrlib::time::tTimestamp last_unfiltered_connection_error = rrlib::time::cNO_TIME;
82const int cMAX_DISPLAYED_CONNECTION_ERRORS = 5;
83const rrlib::time::tDuration cMAX_DISPLAYED_CONNECTION_ERRORS_PERIOD = std::chrono::seconds(10);
84
85}
86
87/*!
88 * Handles accepted connections
89 * We don't use boost bind to minimize overhead
90 */
91class tAcceptHandler
92{
93public:
94  tAcceptHandler(tServer& implementation, std::shared_ptr<boost::asio::ip::tcp::acceptor>& acceptor) :
95    implementation(&implementation),
96    socket(new boost::asio::ip::tcp::socket(implementation.peer.IOService())),
97    acceptor(acceptor)
98  {}
99
100  void operator()(const boost::system::error_code& error)
101  {
102    if (!error)
103    {
104      tConnection::InitConnection(implementation->peer, socket, 0, NULL);
105    }
106    else
107    {
108      auto now = rrlib::time::Now();
109
110      if (now > last_unfiltered_connection_error + cMAX_DISPLAYED_CONNECTION_ERRORS_PERIOD)
111      {
112        if (connection_error_count > cMAX_DISPLAYED_CONNECTION_ERRORS)
113        {
114          FINROC_LOG_PRINT(ERROR, "Filtered ", connection_error_count - cMAX_DISPLAYED_CONNECTION_ERRORS , " additional errors");
115        }
116        connection_error_count = 0;
117      }
118
119      connection_error_count++;
120      if (connection_error_count <= cMAX_DISPLAYED_CONNECTION_ERRORS)
121      {
122        FINROC_LOG_PRINT(ERROR, "Connection error ", error);
123        if (connection_error_count == cMAX_DISPLAYED_CONNECTION_ERRORS)
124        {
125          FINROC_LOG_PRINT(ERROR, "Filtering additional errors...");
126        }
127        last_unfiltered_connection_error = now;
128      }
129    }
130
131    // Accept another connection
132    implementation->StartConnectionAccept(acceptor);
133  }
134
135  tServer* implementation;
136
137  /*! Boost asio server socket */
138  std::shared_ptr<boost::asio::ip::tcp::socket> socket;
139
140  /*! Acceptor pointer */
141  std::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor;
142};
143
144tServer::tServer(tPeerImplementation& peer) :
145  tFrameworkElement(&peer.framework_element, "TCP Server", tFlag::NETWORK_ELEMENT),
146  peer(peer),
147  desired_port(peer.create_options.preferred_server_port),
148  try_next_ports_if_occupied(peer.create_options.try_next_ports_if_occupied),
149  server_listen_address(peer.create_options.server_listen_address)
150{
151}
152
153tServer::~tServer() {}
154
155void tServer::Run()
156{
157  assert(&rrlib::thread::tThread::CurrentThread() == peer.thread.get());
158
159  // Setup server socket
160  std::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor;
161  boost::system::error_code ec;
162
163  boost::asio::ip::address listen_address = boost::asio::ip::address::from_string(server_listen_address);
164
165  for (int port_to_try = desired_port;
166       port_to_try < (try_next_ports_if_occupied ? (desired_port + tSettings::cMAX_PORTS_TO_TRY_FOR_CREATING_SERVER_PORT) : (desired_port + 1));
167       port_to_try++)
168  {
169    try
170    {
171      boost::asio::ip::tcp::endpoint epoint(listen_address, port_to_try);
172      acceptor.reset(new boost::asio::ip::tcp::acceptor(*peer.io_service));
173      acceptor->open(epoint.protocol());
174      acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
175      if (epoint.protocol() == boost::asio::ip::tcp::v6())
176      {
177        acceptor->set_option(boost::asio::ip::v6_only(false), ec);
178        if (ec)
179        {
180          FINROC_LOG_PRINT(WARNING, "Could not enable additional IPv4 support");
181          acceptor.reset();
182          continue;
183        }
184      }
185      acceptor->bind(epoint);
186      acceptor->listen();
187      peer.this_peer.uuid.port = port_to_try;
188      break;
189    }
190    catch (std::exception& ex)
191    {
192      FINROC_LOG_PRINT(WARNING, "Could not listen on port: ", port_to_try, ".");
193      acceptor.reset();
194    }
195  }
196  if (!acceptor)
197  {
198    FINROC_LOG_PRINT(ERROR, "TCP server could not listen on any of the ", tSettings::cMAX_PORTS_TO_TRY_FOR_CREATING_SERVER_PORT, " ports. TCP interface is not available.");
199    return;
200  }
201  FINROC_LOG_PRINT(USER, "TCP server is listening on port ", peer.this_peer.uuid.port);
202
203  // If no connect-address was specified and desired port was occupied - connect to part that is running there
204  if (peer.create_options.auto_connect_to_all_peers && DesiredPort() != peer.this_peer.uuid.port)
205  {
206    peer.connect_to.push_back(std::string("localhost:") + std::to_string(DesiredPort()));
207  }
208
209  // Accept connections on socket
210  StartConnectionAccept(acceptor);
211  peer.io_service->run();
212}
213
214void tServer::StartConnectionAccept(std::shared_ptr<boost::asio::ip::tcp::acceptor>& acceptor)
215{
216  internal::tAcceptHandler accept_handler(*this, acceptor);
217  acceptor->async_accept(*accept_handler.socket, accept_handler);
218}
219
220//----------------------------------------------------------------------
221// End of namespace declaration
222//----------------------------------------------------------------------
223}
224}
225}
Note: See TracBrowser for help on using the repository browser.