Goby v2
iridium_shore_rudics.h
1 // Copyright 2009-2018 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Goby Underwater Autonomy Project Libraries
8 // ("The Goby Libraries").
9 //
10 // The Goby Libraries are free software: you can redistribute them and/or modify
11 // them under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // The Goby Libraries are distributed in the hope that they will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
22 
23 #ifndef IridiumShoreSBD20150508H
24 #define IridiumShoreSBD20150508H
25 
26 #include <boost/asio.hpp>
27 #include <boost/bind.hpp>
28 #include <boost/enable_shared_from_this.hpp>
29 #include <boost/signals2.hpp>
30 
31 #include "goby/common/time.h"
32 #include "goby/util/binary.h"
33 
34 namespace goby
35 {
36 namespace acomms
37 {
38 class RUDICSConnection : public boost::enable_shared_from_this<RUDICSConnection>
39 {
40  public:
41  static boost::shared_ptr<RUDICSConnection> create(boost::asio::io_service& io_service)
42  {
43  return boost::shared_ptr<RUDICSConnection>(new RUDICSConnection(io_service));
44  }
45 
46  boost::asio::ip::tcp::socket& socket() { return socket_; }
47 
48  void start()
49  {
50  remote_endpoint_str_ = boost::lexical_cast<std::string>(socket_.remote_endpoint());
51  read_start();
52  }
53 
54  void close()
55  {
56  socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
57  socket_.close();
58  }
59 
60  void read_start()
61  {
62  boost::asio::async_read_until(socket_, buffer_, '\r',
63  boost::bind(&RUDICSConnection::handle_read, this, _1, _2));
64  }
65 
66  void write_start(const std::string& data)
67  {
68  boost::asio::async_write(socket_, boost::asio::buffer(data),
69  boost::bind(&RUDICSConnection::handle_write, this, _1, _2));
70  }
71 
73  {
74  using goby::glog;
75  using goby::common::logger::DEBUG1;
76  glog.is(DEBUG1) && glog << "Disconnecting from: " << remote_endpoint_str_ << std::endl;
77  }
78 
79  void add_packet_failure()
80  {
81  using goby::glog;
82  using goby::common::logger::DEBUG1;
83  const int max_packet_failures = 3;
84  if (++packet_failures_ >= max_packet_failures)
85  {
86  glog.is(DEBUG1) && glog << "More than " << max_packet_failures << " bad RUDICS packets."
87  << std::endl;
88  close();
89  }
90  }
91 
92  boost::signals2::signal<void(const std::string& line,
93  boost::shared_ptr<RUDICSConnection> connection)>
94  line_signal;
95  boost::signals2::signal<void(boost::shared_ptr<RUDICSConnection> connection)> disconnect_signal;
96 
97  const std::string& remote_endpoint_str() { return remote_endpoint_str_; }
98 
99  private:
100  RUDICSConnection(boost::asio::io_service& io_service)
101  : socket_(io_service), remote_endpoint_str_("Unknown"), packet_failures_(0)
102  {
103  }
104 
105  void handle_write(const boost::system::error_code& error, size_t bytes_transferred)
106  {
107  if (error)
108  {
109  using goby::glog;
110  using goby::common::logger::WARN;
111  glog.is(WARN) && glog << "Error writing to TCP connection: " << error << std::endl;
112  disconnect_signal(shared_from_this());
113  }
114  }
115 
116  void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
117  {
118  using goby::glog;
119  using goby::common::logger::DEBUG1;
120  using goby::common::logger::WARN;
121  if (!error)
122  {
123  std::istream istrm(&buffer_);
124  std::string line;
125  std::getline(istrm, line, '\r');
126  line_signal(line + "\r", shared_from_this());
127  read_start();
128  }
129  else
130  {
131  if (error == boost::asio::error::eof)
132  {
133  glog.is(DEBUG1) && glog << "Connection reached EOF" << std::endl;
134  }
135  else if (error == boost::asio::error::operation_aborted)
136  {
137  glog.is(DEBUG1) && glog << "Read operation aborted (socket closed)" << std::endl;
138  }
139  else
140  {
141  glog.is(WARN) && glog << "Error reading from TCP connection: " << error
142  << std::endl;
143  }
144 
145  disconnect_signal(shared_from_this());
146  }
147  }
148 
149  private:
150  boost::asio::ip::tcp::socket socket_;
151  boost::asio::streambuf buffer_;
152  std::string remote_endpoint_str_;
153  int packet_failures_;
154 };
155 
157 {
158  public:
159  RUDICSServer(boost::asio::io_service& io_service, int port)
160  : acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
161  {
162  start_accept();
163  }
164 
165  std::set<boost::shared_ptr<RUDICSConnection> >& connections() { return connections_; }
166 
167  boost::signals2::signal<void(boost::shared_ptr<RUDICSConnection> connection)> connect_signal;
168 
169  void disconnect(boost::shared_ptr<RUDICSConnection> connection) { connection->close(); }
170 
171  private:
172  void start_accept()
173  {
174  boost::shared_ptr<RUDICSConnection> new_connection =
175  RUDICSConnection::create(acceptor_.get_io_service());
176  acceptor_.async_accept(new_connection->socket(),
177  boost::bind(&RUDICSServer::handle_accept, this, new_connection,
178  boost::asio::placeholders::error));
179  }
180 
181  void handle_accept(boost::shared_ptr<RUDICSConnection> new_connection,
182  const boost::system::error_code& error)
183  {
184  if (!error)
185  {
186  using namespace goby::common::logger;
187  using goby::glog;
188 
189  connections_.insert(new_connection);
190 
191  new_connection->disconnect_signal.connect(
192  boost::bind(&RUDICSServer::handle_disconnect, this, _1));
193  connect_signal(new_connection);
194  new_connection->start();
195  glog.is(DEBUG1) && glog << "Received connection from: "
196  << new_connection->remote_endpoint_str() << std::endl;
197  }
198 
199  start_accept();
200  }
201 
202  void handle_disconnect(boost::shared_ptr<RUDICSConnection> connection)
203  {
204  using goby::glog;
205  using goby::common::logger::DEBUG1;
206 
207  connections_.erase(connection);
208 
209  glog.is(DEBUG1) &&
210  glog << "Server removing connection: " << connection->remote_endpoint_str()
211  << ". Remaining connection count: " << connections_.size() << std::endl;
212  }
213 
214  std::set<boost::shared_ptr<RUDICSConnection> > connections_;
215  boost::asio::ip::tcp::acceptor acceptor_;
216 };
217 
218 } // namespace acomms
219 } // namespace goby
220 
221 #endif
common::FlexOstream glog
Access the Goby logger through this object.
The global namespace for the Goby project.
void disconnect(Signal *signal, Slot slot)
disconnect a signal to a slot (e.g. function pointer)
Definition: connect.h:63