OrocosComponentLibrary  2.9.0
socket.cpp
1 /***************************************************************************
2 
3  Socket.cpp - Small socket wrapper
4  -------------------
5  begin : Fri Aug 4 2006
6  copyright : (C) 2006 Bas Kemper
7  email : kst@ <my name> .be
8 
9  ***************************************************************************
10  * This library is free software; you can redistribute it and/or *
11  * modify it under the terms of the GNU Lesser General Public *
12  * License as published by the Free Software Foundation; either *
13  * version 2.1 of the License, or (at your option) any later version. *
14  * *
15  * This library is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
18  * Lesser General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU Lesser General Public *
21  * License along with this library; if not, write to the Free Software *
22  * Foundation, Inc., 59 Temple Place, *
23  * Suite 330, Boston, MA 02111-1307 USA *
24  * *
25  ***************************************************************************/
26 
27 #include <cstdio>
28 #include <sys/socket.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <rtt/Logger.hpp>
32 #include <string.h>
33 #include "socket.hpp"
34 
35 using RTT::Logger;
36 
37 #define MSGLENGTH 100
38 #if MSGLENGTH * 3 > BUFLENGTH
39  #error "MSGLENGTH too long" /* memcpy is used */
40 #endif
41 
42 #if __APPLE__
43 #define SEND_OPTIONS 0
44 #else
45 #define SEND_OPTIONS MSG_NOSIGNAL
46 #endif
47 
48 namespace {
49  const unsigned int bufsize = 2048;
50  class sockbuf : public std::streambuf
51  {
52  private:
53  char* ptr;
54  OCL::TCP::Socket* mainClass;
55 
56  public:
57  sockbuf( OCL::TCP::Socket* m ) : mainClass(m)
58  {
59  char* ptr = new char[bufsize];
60  setp(ptr, ptr + bufsize); // output buffer
61  setg(0, 0, 0); // input stream: not enabled
62 #if __APPLE__
63  /* Linux uses MSG_NOSIGNAL on the ::send() calls, but Mac OS X
64  supports this as a socket option with SIG_NOSIGPIPE. Just
65  set the socket now with that option and not worry about the
66  MSG_NOSIGNAL's later in each of the send() calls. For
67  further details, see
68 http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html
69 http://trac.wxwidgets.org/ticket/7150
70 http://gobby.0x539.de/trac/browser/net6/trunk/src/socket.cpp?rev=224
71  */
72  int value = 1;
73  if (-1 == setsockopt(
74  mainClass->socket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)))
75  {
76  Logger::log() << Logger::Error << "Error setting socket option. Continuing." << Logger::endl;
77  }
78  #endif
79  }
80 
81  ~sockbuf()
82  {
83  sync();
84  delete[] ptr;
85  }
86 
87  int overflow(int c)
88  {
89  int ret = 0;
90  put_buffer();
91 
92  if (c != EOF)
93  {
94  if (pbase() == epptr())
95  {
96  put_char(c);
97  } else {
98  ret = sputc(c);
99  }
100  } else {
101  ret = EOF;
102  }
103  return ret;
104  }
105 
106  int sync()
107  {
108  put_buffer();
109  return 0;
110  }
111 
112  void put_char(int chr)
113  {
114  Logger::log() << Logger::Error << "Socket::put_char is unimplemented" << Logger::endl;
115  }
116 
117  void put_buffer()
118  {
119  if (pbase() != pptr())
120  {
121  int length = (pptr() - pbase());
122  char *buffer = new char[length + 1];
123 
124  strncpy(buffer, pbase(), length);
125  buffer[length] = '\0';
126 
127  // empty strings are not sent
128  if( length && ::send ( mainClass->socket, buffer, length, SEND_OPTIONS ) == -1 )
129  {
130  mainClass->rawClose();
131  }
132  setp(pbase(), epptr());
133  delete[] buffer;
134  }
135  }
136  };
137 };
138 
139 namespace OCL {
140 namespace TCP {
141  Socket::Socket( int socketID ) :
142  std::ostream( new sockbuf(this) ),
143  socket(socketID), begin(0), ptrpos(0), end(0)
144  {
145  }
146 
147 
148  Socket::~Socket()
149  {
150  if( isValid() )
151  {
152  rawClose();
153  }
154  }
155 
156  bool Socket::isValid() const
157  {
158  return socket >= 0;
159  }
160 
162  {
163  return isValid() && lineAvailable();
164  }
165 
166  bool Socket::lineAvailable()
167  {
168  int flags = fcntl(socket,F_GETFL);
169  fcntl(socket,F_SETFL,flags | O_NONBLOCK);
170  int ret =recv(socket,buffer,MSGLENGTH,MSG_PEEK);
171  if(ret>0){
172  //search for \n or \0
173  for(unsigned int i=0;i<MSGLENGTH;++i)
174  if( buffer[i] == '\n'){
175  ptrpos=i;
176  return true;
177  }
178  return false;
179  }else if(ret==0){
180  rawClose();
181  }
182  return false;
183 
184 
185  /* this if clause allows calling lineAvailable() multiple times
186  without reading the actual lines with readLine(). */
187  /*
188  if( ptrpos < end && buffer[ptrpos] == '\0' ){
189  return true;
190  }
191 
192  while( ptrpos < end ){
193  if( buffer[ptrpos] == '\n' ) {
194  // overwrite the \n or \r\n with \0
195  if( begin < ptrpos && buffer[ptrpos-1] == '\r' ){
196  buffer[ptrpos-1] = '\0';
197  }
198  buffer[ptrpos] = '\0';
199  return true;
200  }
201  ++ptrpos;
202  }
203  return false;
204  */
205  }
206 
207  void Socket::checkBufferOverflow()
208  {
209  if( end + MSGLENGTH >= BUFLENGTH ) {
210  if( ptrpos - begin > MSGLENGTH ) {
211  Logger::log() << Logger::Error << "Message length violation" << Logger::endl;
212  rawClose();
213  } else {
214  memcpy( buffer, &buffer[begin], end - begin);
215  }
216  end -= begin;
217  ptrpos -= begin;
218  begin = 0;
219  }
220  }
221 
222  std::string Socket::readLine()
223  {
224  if(dataAvailable()){
225  if(0>recv(socket,buffer,sizeof(char[ptrpos+1]),MSG_WAITALL))
226  return "";
227 
228  return std::string(buffer,ptrpos);
229  }
230  return "";
231  /* ugly C style code to read a line from the socket */
232 
233 
234  /*
235  while(isValid()){
236  // process remaining full lines in the buffer
237  if( lineAvailable() ){
238  std::string ret(&buffer[begin]);
239 
240  if( begin == end - 1 ){
241  // reset to start of buffer when everything is read
242  begin = 0;
243  end = 0;
244  ptrpos = 0;
245  } else {
246  ++ptrpos;
247  begin = ptrpos;
248  }
249  return ret;
250  }
251 
252  // move data back to the beginning of the buffer (should not occur very often)
253  checkBufferOverflow();
254 
255 
256  // wait for additional input
257  int received = recv(socket, &buffer[end], MSGLENGTH, 0 );
258  if( received == 0 || received == -1 ){
259  rawClose();
260  return "";
261  }
262  end += received;
263  }
264  return "";
265  */
266  }
267 
268  void Socket::rawClose()
269  {
270  if( socket != -1 )
271  {
272  ::close(socket);
273  }
274  socket = -1;
275  return;
276  }
277 
279  {
280  int _socket = socket;
281  socket = -1;
282 
283  if( _socket )
284  {
285  // The user notification is sent non-blocking.
286  int flags = fcntl( _socket, F_GETFL, 0 );
287  if( flags == -1 )
288  {
289  flags = 0;
290  }
291  fcntl( _socket, F_SETFL, flags | O_NONBLOCK );
292  ::send ( _socket, "104 Bye bye", 11, SEND_OPTIONS );
293  ::close( _socket );
294  }
295  }
296 };
297 };
void close()
Close the connection.
Definition: socket.cpp:278
STL namespace.
The Orocos Component Library.
Definition: Component.hpp:43
std::string readLine()
Read a line from the socket.
Definition: socket.cpp:222
bool isValid() const
Check wether the state of the socket is valid or not.
Definition: socket.cpp:156
bool dataAvailable()
Check wether there is new data available.
Definition: socket.cpp:161