OrocosComponentLibrary  2.7.0
socket.cpp
00001 /***************************************************************************
00002 
00003                       Socket.cpp -  Small socket wrapper
00004                            -------------------
00005     begin                : Fri Aug 4 2006
00006     copyright            : (C) 2006 Bas Kemper
00007     email                : kst@ <my name> .be
00008 
00009  ***************************************************************************
00010  *   This library is free software; you can redistribute it and/or         *
00011  *   modify it under the terms of the GNU Lesser General Public            *
00012  *   License as published by the Free Software Foundation; either          *
00013  *   version 2.1 of the License, or (at your option) any later version.    *
00014  *                                                                         *
00015  *   This library is distributed in the hope that it will be useful,       *
00016  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00018  *   Lesser General Public License for more details.                       *
00019  *                                                                         *
00020  *   You should have received a copy of the GNU Lesser General Public      *
00021  *   License along with this library; if not, write to the Free Software   *
00022  *   Foundation, Inc., 59 Temple Place,                                    *
00023  *   Suite 330, Boston, MA  02111-1307  USA                                *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 #include <cstdio>
00028 #include <sys/socket.h>
00029 #include <fcntl.h>
00030 #include <errno.h>
00031 #include <rtt/Logger.hpp>
00032 #include <string.h>
00033 #include "socket.hpp"
00034 
00035 using RTT::Logger;
00036 
00037 #define MSGLENGTH 100
00038 #if MSGLENGTH * 3 > BUFLENGTH
00039     #error "MSGLENGTH too long" /* memcpy is used */
00040 #endif
00041 
00042 #if __APPLE__
00043 #define SEND_OPTIONS        0
00044 #else
00045 #define SEND_OPTIONS        MSG_NOSIGNAL
00046 #endif
00047 
00048 namespace {
00049     const unsigned int bufsize = 2048;
00050     class sockbuf : public std::streambuf
00051     {
00052         private:
00053             char* ptr;
00054             OCL::TCP::Socket* mainClass;
00055 
00056         public:
00057             sockbuf( OCL::TCP::Socket* m ) : mainClass(m)
00058             {
00059                 char* ptr = new char[bufsize];
00060                 setp(ptr, ptr + bufsize);   // output buffer
00061                 setg(0, 0, 0);              // input stream: not enabled
00062 #if __APPLE__
00063                 /* Linux uses MSG_NOSIGNAL on the ::send() calls, but Mac OS X
00064                    supports this as a socket option with SIG_NOSIGPIPE. Just
00065                    set the socket now with that option and not worry about the
00066                    MSG_NOSIGNAL's later in each of the send() calls. For 
00067                    further details, see
00068 http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html
00069 http://trac.wxwidgets.org/ticket/7150
00070 http://gobby.0x539.de/trac/browser/net6/trunk/src/socket.cpp?rev=224
00071                 */
00072                 int value = 1;
00073                 if (-1 == setsockopt(
00074                         mainClass->socket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)))
00075                 {
00076                     Logger::log() << Logger::Error << "Error setting socket option. Continuing." << Logger::endl;
00077                 }
00078  #endif
00079             }
00080 
00081             ~sockbuf()
00082             {
00083                 sync();
00084                 delete[] ptr;
00085             }
00086 
00087             int overflow(int c)
00088             {
00089                 int ret = 0;
00090                 put_buffer();
00091 
00092                 if (c != EOF)
00093                 {
00094                     if (pbase() == epptr())
00095                     {
00096                         put_char(c);
00097                     } else {
00098                         ret = sputc(c);
00099                     }
00100                 } else {
00101                     ret = EOF;
00102                 }
00103                 return ret;
00104             }
00105 
00106             int sync()
00107             {
00108                 put_buffer();
00109                 return 0;
00110             }
00111 
00112             void put_char(int chr)
00113             {
00114                 Logger::log() << Logger::Error << "Socket::put_char is unimplemented" << Logger::endl;
00115             }
00116 
00117             void put_buffer()
00118             {
00119                 if (pbase() != pptr())
00120                 {
00121                     int length = (pptr() - pbase());
00122                     char *buffer = new char[length + 1];
00123 
00124                     strncpy(buffer, pbase(), length);
00125                     buffer[length] = '\0';
00126 
00127                     // empty strings are not sent
00128                     if( length && ::send ( mainClass->socket, buffer, length, SEND_OPTIONS ) == -1 )
00129                     {
00130                         mainClass->rawClose();
00131                     }
00132                     setp(pbase(), epptr());
00133                     delete[] buffer;
00134                 }
00135             }
00136     };
00137 };
00138 
00139 namespace OCL {
00140 namespace TCP {
00141     Socket::Socket( int socketID ) :
00142             std::ostream( new sockbuf(this) ),
00143             socket(socketID), begin(0), ptrpos(0), end(0)
00144     {
00145     }
00146 
00147 
00148     Socket::~Socket()
00149     {
00150         if( isValid() )
00151         {
00152             rawClose();
00153         }
00154     }
00155 
00156     bool Socket::isValid() const
00157     {
00158         return socket >= 0;
00159     }
00160 
00161     bool Socket::dataAvailable()
00162     {
00163         return isValid() && lineAvailable();
00164     }
00165 
00166     bool Socket::lineAvailable()
00167     {
00168         int flags = fcntl(socket,F_GETFL);
00169         fcntl(socket,F_SETFL,flags | O_NONBLOCK);
00170         int ret =recv(socket,buffer,MSGLENGTH,MSG_PEEK);
00171         if(ret>0){
00172             //search for \n or \0
00173             for(unsigned int i=0;i<MSGLENGTH;++i)
00174                 if( buffer[i] == '\n'){
00175                     ptrpos=i;
00176                     return true;
00177                 }
00178             return false;
00179         }else if(ret==0){
00180             rawClose();
00181         }
00182         return false;
00183 
00184 
00185         /* this if clause allows calling lineAvailable() multiple times
00186         without reading the actual lines with readLine(). */
00187         /*
00188         if( ptrpos < end && buffer[ptrpos] == '\0' ){
00189             return true;
00190         }
00191 
00192         while( ptrpos < end ){
00193             if( buffer[ptrpos] == '\n' ) {
00194                 //   overwrite the \n or \r\n with \0
00195                 if( begin < ptrpos && buffer[ptrpos-1] == '\r' ){
00196                     buffer[ptrpos-1] = '\0';
00197                 }
00198                 buffer[ptrpos] = '\0';
00199                 return true;
00200             }
00201             ++ptrpos;
00202         }
00203         return false;
00204         */
00205     }
00206 
00207     void Socket::checkBufferOverflow()
00208     {
00209         if( end + MSGLENGTH >= BUFLENGTH ) {
00210             if( ptrpos - begin > MSGLENGTH ) {
00211                 Logger::log() << Logger::Error << "Message length violation" << Logger::endl;
00212                 rawClose();
00213             } else {
00214                 memcpy( buffer, &buffer[begin], end - begin);
00215             }
00216             end -= begin;
00217             ptrpos -= begin;
00218             begin = 0;
00219         }
00220     }
00221 
00222     std::string Socket::readLine()
00223     {
00224         if(dataAvailable()){
00225             if(0>recv(socket,buffer,sizeof(char[ptrpos+1]),MSG_WAITALL))
00226                 return "";
00227 
00228             return std::string(buffer,ptrpos);
00229         }
00230         return "";
00231         /* ugly C style code to read a line from the socket */
00232 
00233 
00234         /*
00235             while(isValid()){
00236                 // process remaining full lines in the buffer
00237             if( lineAvailable() ){
00238                 std::string ret(&buffer[begin]);
00239 
00240                 if( begin == end - 1 ){
00241                     // reset to start of buffer when everything is read
00242                     begin = 0;
00243                     end = 0;
00244                     ptrpos = 0;
00245                 } else {
00246                     ++ptrpos;
00247                     begin = ptrpos;
00248                 }
00249                 return ret;
00250             }
00251 
00252             // move data back to the beginning of the buffer (should not occur very often)
00253             checkBufferOverflow();
00254 
00255 
00256             // wait for additional input
00257             int received = recv(socket, &buffer[end], MSGLENGTH, 0 );
00258             if( received == 0 || received == -1 ){
00259                 rawClose();
00260                 return "";
00261             }
00262             end += received;
00263         }
00264         return "";
00265         */
00266     }
00267 
00268     void Socket::rawClose()
00269     {
00270         if( socket != -1 )
00271         {
00272             ::close(socket);
00273         }
00274         socket = -1;
00275         return;
00276     }
00277 
00278     void Socket::close()
00279     {
00280         int _socket = socket;
00281         socket = -1;
00282 
00283         if( _socket )
00284         {
00285             // The user notification is sent non-blocking.
00286             int flags = fcntl( _socket, F_GETFL, 0 );
00287             if( flags == -1 )
00288             {
00289                 flags = 0;
00290             }
00291             fcntl( _socket, F_SETFL, flags | O_NONBLOCK );
00292             ::send ( _socket, "104 Bye bye", 11, SEND_OPTIONS );
00293             ::close( _socket );
00294         }
00295     }
00296 };
00297 };