source: finroc_plugins_tcp/internal/tServer.cpp @ 184:675dfcc4d494

17.03
Last change on this file since 184:675dfcc4d494 was 184:675dfcc4d494, checked in by Max Reichardt <max.reichardt@…>, 3 years ago

Merge with 14.08

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/internal/tConnection.h"
42#include "plugins/tcp/internal/tPeerImplementation.h"
43#include "plugins/tcp/internal/util.h"
44
45//----------------------------------------------------------------------
46// Debugging
47//----------------------------------------------------------------------
48#include <cassert>
49
50//----------------------------------------------------------------------
51// Namespace usage
52//----------------------------------------------------------------------
53
54//----------------------------------------------------------------------
55// Namespace declaration
56//----------------------------------------------------------------------
57namespace finroc
58{
59namespace tcp
60{
61namespace internal
62{
63
64//----------------------------------------------------------------------
65// Forward declarations / typedefs / enums
66//----------------------------------------------------------------------
67
68//----------------------------------------------------------------------
69// Const values
70//----------------------------------------------------------------------
71
72//----------------------------------------------------------------------
73// Implementation
74//----------------------------------------------------------------------
75
76namespace
77{
78
79size_t connection_error_count = 0;
80rrlib::time::tTimestamp last_unfiltered_connection_error = rrlib::time::cNO_TIME;
81const int cMAX_DISPLAYED_CONNECTION_ERRORS = 5;
82const rrlib::time::tDuration cMAX_DISPLAYED_CONNECTION_ERRORS_PERIOD = std::chrono::seconds(10);
83
84}
85
86/*!
87 * Handles accepted connections
88 * We don't use boost bind to minimize overhead
89 */
90class tAcceptHandler
91{
92public:
93  tAcceptHandler(tServer& implementation, std::shared_ptr<boost::asio::ip::tcp::acceptor>& acceptor) :
94    implementation(&implementation),
95    socket(new boost::asio::ip::tcp::socket(implementation.peer.IOService())),
96    acceptor(acceptor)
97  {}
98
99  void operator()(const boost::system::error_code& error)
100  {
101    if (!error)
102    {
103      tConnection::TryToEstablishConnection(implementation->peer, socket, 0, nullptr);
104    }
105    else
106    {
107      auto now = rrlib::time::Now();
108
109      if (now > last_unfiltered_connection_error + cMAX_DISPLAYED_CONNECTION_ERRORS_PERIOD)
110      {
111        if (connection_error_count > cMAX_DISPLAYED_CONNECTION_ERRORS)
112        {
113          FINROC_LOG_PRINT(ERROR, "Filtered ", connection_error_count - cMAX_DISPLAYED_CONNECTION_ERRORS , " additional errors");
114        }
115        connection_error_count = 0;
116      }
117
118      connection_error_count++;
119      if (connection_error_count <= cMAX_DISPLAYED_CONNECTION_ERRORS)
120      {
121        FINROC_LOG_PRINT(ERROR, "Error accepting TCP connection: ", error);
122        if (connection_error_count == cMAX_DISPLAYED_CONNECTION_ERRORS)
123        {
124          FINROC_LOG_PRINT(ERROR, "Filtering additional errors...");
125        }
126        last_unfiltered_connection_error = now;
127      }
128    }
129
130    // Accept another connection
131    implementation->StartConnectionAccept(acceptor);
132  }
133
134  tServer* implementation;
135
136  /*! Boost asio server socket */
137  std::shared_ptr<boost::asio::ip::tcp::socket> socket;
138
139  /*! Acceptor pointer */
140  std::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor;
141};
142
143tServer::tServer(tPeerImplementation& peer) :
144  tFrameworkElement(peer.GetPluginRootFrameworkElement(), "TCP Server", tFlag::NETWORK_ELEMENT),
145  peer(peer)
146{
147}
148
149tServer::~tServer() {}
150
151void tServer::Run()
152{
153  assert(&rrlib::thread::tThread::CurrentThread() == peer.thread.get());
154
155  // Setup server socket
156  std::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor;
157  boost::system::error_code ec;
158
159  boost::asio::ip::address listen_address = boost::asio::ip::address::from_string(peer.par_server_listen_address.Get());
160
161  auto desired_port = peer.par_preferred_server_port.Get();
162  bool try_next_ports_if_occupied = peer.par_try_next_ports_if_occupied.Get();
163  for (int port_to_try = desired_port;
164       port_to_try < (try_next_ports_if_occupied ? (desired_port + peer.par_max_ports_to_try_creating_server_port.Get()) : (desired_port + 1));
165       port_to_try++)
166  {
167    try
168    {
169      boost::asio::ip::tcp::endpoint epoint(listen_address, port_to_try);
170      acceptor.reset(new boost::asio::ip::tcp::acceptor(*peer.io_service));
171      acceptor->open(epoint.protocol());
172      acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
173      if (epoint.protocol() == boost::asio::ip::tcp::v6())
174      {
175        acceptor->set_option(boost::asio::ip::v6_only(false), ec);
176        if (ec)
177        {
178          FINROC_LOG_PRINT(WARNING, "Could not enable additional IPv4 support");
179          acceptor.reset();
180          continue;
181        }
182      }
183      acceptor->bind(epoint);
184      acceptor->listen();
185      peer.this_peer.uuid.port = port_to_try;
186      break;
187    }
188    catch (std::exception& ex)
189    {
190      FINROC_LOG_PRINT(WARNING, "Could not listen on port: ", port_to_try, ".");
191      acceptor.reset();
192    }
193  }
194  if (!acceptor)
195  {
196    FINROC_LOG_PRINT(ERROR, "TCP server could not listen on any of the ", peer.par_max_ports_to_try_creating_server_port.Get(), " ports tried. TCP interface is not available.");
197    return;
198  }
199  FINROC_LOG_PRINT(USER, "TCP server is listening on port ", peer.this_peer.uuid.port);
200
201  // If no connect-address was specified and desired port was occupied - connect to part that is running there
202  if (peer.par_auto_connect_to_all_peers.Get() && desired_port != peer.this_peer.uuid.port)
203  {
204    peer.AddRuntimeToConnectTo(std::string("localhost:") + std::to_string(desired_port));
205  }
206
207  // Accept connections on socket
208  StartConnectionAccept(acceptor);
209  peer.io_service->run();
210}
211
212void tServer::StartConnectionAccept(std::shared_ptr<boost::asio::ip::tcp::acceptor>& acceptor)
213{
214  internal::tAcceptHandler accept_handler(*this, acceptor);
215  acceptor->async_accept(*accept_handler.socket, accept_handler);
216}
217
218//----------------------------------------------------------------------
219// End of namespace declaration
220//----------------------------------------------------------------------
221}
222}
223}
Note: See TracBrowser for help on using the repository browser.