$search
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 };