OrocosComponentLibrary  2.8.3
TcpReporting.cpp
1 /***************************************************************************
2 
3  TcpReporting.cpp - TCP reporter
4  -------------------
5  begin : Fri Aug 4 2006
6  copyright : (C) 2006 Bas Kemper
7  2007-2008 Ruben Smits
8  email : kst@ <my name> .be
9 
10  ***************************************************************************
11  * This library is free software; you can redistribute it and/or *
12  * modify it under the terms of the GNU Lesser General Public *
13  * License as published by the Free Software Foundation; either *
14  * version 2.1 of the License, or (at your option) any later version. *
15  * *
16  * This library is distributed in the hope that it will be useful, *
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19  * Lesser General Public License for more details. *
20  * *
21  * You should have received a copy of the GNU Lesser General Public *
22  * License along with this library; if not, write to the Free Software *
23  * Foundation, Inc., 59 Temple Place, *
24  * Suite 330, Boston, MA 02111-1307 USA *
25  * *
26  ***************************************************************************/
27 
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <sys/types.h>
31 #include <errno.h>
32 
33 #include "TcpReporting.hpp"
34 #include <rtt/Activity.hpp>
35 #include <rtt/Logger.hpp>
36 #include <rtt/os/Mutex.hpp>
37 #include "socket.hpp"
38 #include "socketmarshaller.hpp"
39 
40 using RTT::Logger;
41 using RTT::os::Mutex;
42 
43 #include "ocl/Component.hpp"
44 ORO_LIST_COMPONENT_TYPE(OCL::TcpReporting);
45 
46 namespace OCL
47 {
53  : public RTT::Activity
54  {
55  private:
56  bool inBreak;
57  static ListenThread* _instance;
58  RTT::SocketMarshaller* _marshaller;
59  unsigned short _port;
60  bool _accepting;
61  int _sock;
62 
63  bool listen()
64  {
65  _sock = ::socket(PF_INET, SOCK_STREAM, 0);
66  if( _sock < 0 )
67  {
68  Logger::log() << Logger::Error << "Socket creation failed." << Logger::endl;
69  return false;
70  }
71 
72  struct sockaddr_in localsocket;
73  struct sockaddr remote;
74  int adrlen = sizeof(remote);
75 
76  localsocket.sin_family = AF_INET;
77  localsocket.sin_port = htons(_port);
78  localsocket.sin_addr.s_addr = INADDR_ANY;
79  if( ::bind(_sock, (struct sockaddr*)&localsocket, sizeof(localsocket) ) < 0 )
80  {
81  /* bind can fail when there is a legitimate server when a
82  previous run of orocos has crashed and the kernel does
83  not have freed the port yet. TRY_OTHER_PORTS can
84  select another port if the bind fails. */
85  #define TRY_OTHER_PORTS
86  // TODO: remove #define
87  #ifdef TRY_OTHER_PORTS
88  int i = 1;
89  int r = -1;
90  while( errno == EADDRINUSE && i < 5 && r < 0 )
91  {
92  localsocket.sin_port = htons(_port + i);
93  r = ::bind(_sock, (struct sockaddr*)&localsocket, sizeof(localsocket) );
94  i++;
95  }
96  if( r >= 0 )
97  {
98  Logger::log() << Logger::Info << "Port occupied, use port " << (_port+i-1) << " instead." << Logger::endl;
99  } else {
100  #endif
101  if( errno == EADDRINUSE )
102  {
103  Logger::log() << Logger::Error << "Binding of port failed: address already in use." << Logger::endl;
104  } else {
105  Logger::log() << Logger::Error << "Binding of port failed with errno " << errno << Logger::endl;
106  }
107  ::close(_sock);
108  return false;
109  #ifdef TRY_OTHER_PORTS
110  }
111  #endif
112  }
113 
114  if( ::listen(_sock, 2) < 0 )
115  {
116  Logger::log() << Logger::Info << "Cannot listen on socket" << Logger::endl;
117  ::close(_sock);
118  return true;
119  }
120  while(_accepting)
121  {
122  int socket = ::accept( _sock, &remote,
123  reinterpret_cast<socklen_t*>(&adrlen) );
124  if( socket == -1 )
125  {
126  return false;
127  }
128  if( _accepting )
129  {
130  Logger::log() << Logger::Info << "Incoming connection" << Logger::endl;
131  _marshaller->addConnection( new Orocos::TCP::Socket(socket) );
132  }
133  }
134  return true;
135  }
136 
137  ListenThread( RTT::SocketMarshaller* marshaller, unsigned short port )
138  : Activity(10), _marshaller(marshaller)
139  {
140  inBreak = false;
141  removeInstance();
142  _accepting = true;
143  _port = port;
144  Logger::log() << Logger::Info << "Starting server on port " << port << Logger::endl;
145  this->Activity::start();
146  }
147 
148  // This method should only be called when theadCreationLock is locked.
149  void removeInstance()
150  {
151  if( _instance )
152  {
153  delete _instance;
154  }
155  }
156 
157  public:
158  ~ListenThread()
159  {
160  _accepting = false;
161  }
162 
163  virtual void loop()
164  {
165  if( !inBreak )
166  {
167  if( !listen() )
168  {
169  Logger::log() << Logger::Error << "Could not listen on port " << _port << Logger::endl;
170  } else {
171  Logger::log() << Logger::Info << "Shutting down server" << Logger::endl;
172  }
173  }
174  }
175 
176  virtual bool breakLoop()
177  {
178  inBreak = true;
179  _accepting = false;
180  ::close( _sock );
181  // accept still hangs until a new connection has been established
182  int sock = ::socket(PF_INET, SOCK_STREAM, 0);
183  if( sock > 0 )
184  {
185  struct sockaddr_in socket;
186  socket.sin_family = AF_INET;
187  socket.sin_port = htons(_port);
188  socket.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
189  ::connect( sock, (struct sockaddr*)&socket, sizeof(socket) );
190  ::close( sock );
191  }
192  return true;
193  }
194 
195  static void createInstance( RTT::SocketMarshaller* marshaller, unsigned short port = 3142 )
196  {
197  // The lock is needed to avoid problems when createInstance is called by two
198  // different threads (which in reality should not occur very often).
199  //ListenThread* _oinst = ListenThread::_instance;
200  ListenThread::_instance = new ListenThread( marshaller, port );
201  //delete _oinst;
202  }
203 
204  static void destroyInstance()
205  {
206  ListenThread::_instance->breakLoop();
207  }
208  };
209  ListenThread* ListenThread::_instance = 0;
210 }
211 
212 namespace OCL
213 {
214  TcpReporting::TcpReporting(std::string fr_name /*= "Reporting"*/)
215  : ReportingComponent( fr_name ),
216  port_prop("port","port to listen/send to",3142)
217  {
218  _finishing = false;
219  this->properties()->addProperty( port_prop);
220  }
221 
222  TcpReporting::~TcpReporting()
223  {
224  }
225 
226  const RTT::PropertyBag* TcpReporting::getReport()
227  {
228  makeReport2();
229  return &report;
230  }
231 
233  port=port_prop.value();
234  return true;
235  }
236 
237  bool TcpReporting::startHook()
238  {
239  RTT::Logger::In in("TcpReporting::startup");
240  fbody = new RTT::SocketMarshaller(this);
241  this->addMarshaller( 0, fbody );
242  ListenThread::createInstance( fbody, port );
243  return ReportingComponent::startHook();
244  }
245 
246  void TcpReporting::stopHook()
247  {
248  _finishing = true;
249  ListenThread::destroyInstance();
250  fbody->shutdown();
251  ReportingComponent::stopHook();
252  this->removeMarshallers();
253  }
254 }
bool addMarshaller(RTT::marsh::MarshallInterface *headerM, RTT::marsh::MarshallInterface *bodyM)
Adds a Plugin to receive incomming data.
A component which writes data reports to a tcp/ip socket.
marsh::MarshallInterface which sends data to multiple sockets.
This file contains the macros and definitions to create dynamically loadable components.
bool removeMarshallers()
Remove and delete all added Marshallers.
The Orocos Component Library.
Definition: Component.hpp:43
bool configureHook()
Implementation of base::TaskCore::configureHook().
A Component for periodically reporting Component Port contents to a human readable text format...
TcpReporting(std::string fr_name="ReportingComponent")
Create a reporting component which starts up a server.
const RTT::PropertyBag * getReport()
Return a property bag.
RTT::SocketMarshaller * fbody
marsh::MarshallInterface
ListenThread is a thread which waits for new incoming connections from clients.