TcpSocket.cc

Go to the documentation of this file.
00001 // TcpSocket.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004,2005 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <ctype.h>
00034 #include <sys/types.h>
00035 #include <sys/socket.h>
00036 #include <netinet/in.h>
00037 #include <arpa/inet.h>
00038 #include <netdb.h>
00039 #include <errno.h>
00040 #include <fcntl.h>
00041 
00042 #include "TcpSocket.h"
00043 #include "SocketConfig.h"
00044 #include "SocketException.h"
00045 
00046 void
00047 TcpSocket::connect()
00048 {
00049     if( _listening )
00050     {
00051         string err( "Socket is already listening" ) ;
00052         throw SocketException( err, __FILE__, __LINE__ ) ;
00053     }
00054 
00055     if( _connected )
00056     {
00057         string err( "Socket is already connected" ) ;
00058         throw SocketException( err, __FILE__, __LINE__ ) ;
00059     }
00060 
00061     if( _host == "" )
00062         _host = "localhost" ;
00063 
00064     struct protoent *pProtoEnt ;
00065     struct sockaddr_in sin ;
00066     struct hostent *ph ;
00067     long address ;
00068     if( isdigit( _host[0] ) )
00069     {
00070         if( ( address = inet_addr( _host.c_str() ) ) == -1 )
00071         {
00072             string err( "Invalid host ip address " ) ;
00073             err += _host ;
00074             throw SocketException( err, __FILE__, __LINE__ ) ;
00075         }
00076         sin.sin_addr.s_addr = address ;
00077         sin.sin_family = AF_INET ;
00078     }
00079     else
00080     {
00081         if( ( ph = gethostbyname( _host.c_str() ) ) == NULL )
00082         {
00083             switch( h_errno )
00084             {
00085                 case HOST_NOT_FOUND:
00086                     {
00087                         string err( "No such host " ) ;
00088                         err += _host ;
00089                         throw SocketException( err, __FILE__, __LINE__ ) ;
00090                     }
00091                 case TRY_AGAIN:
00092                     {
00093                         string err( "Host " ) ;
00094                         err += _host + " is busy, try again later" ;
00095                         throw SocketException( err, __FILE__, __LINE__ ) ;
00096                     }
00097                 case NO_RECOVERY:
00098                     {
00099                         string err( "DNS error for host " ) ;
00100                         err += _host ;
00101                         throw SocketException( err, __FILE__, __LINE__ ) ;
00102                     }
00103                 case NO_ADDRESS:
00104                     {
00105                         string err( "No IP address for host " ) ;
00106                         err += _host ;
00107                         throw SocketException( err, __FILE__, __LINE__ ) ;
00108                     }
00109                 default:
00110                     {
00111                         throw SocketException( "unknown error", __FILE__, __LINE__ ) ;
00112                     }
00113             }
00114         }
00115         else
00116         {
00117             sin.sin_family = ph->h_addrtype ;
00118             for( char **p =ph->h_addr_list; *p != NULL; p++ )
00119             {
00120                 struct in_addr in ;
00121                 (void)memcpy( &in.s_addr, *p, sizeof( in.s_addr ) ) ;
00122                 memcpy( (char*)&sin.sin_addr, (char*)&in, sizeof( in ) ) ;
00123             }
00124         }
00125     }
00126 
00127     sin.sin_port = htons( _portVal ) ;
00128     pProtoEnt = getprotobyname( "tcp" ) ;
00129     if( !pProtoEnt )
00130     {
00131         string err( "Error retreiving tcp protocol information" ) ;
00132         throw SocketException( err, __FILE__, __LINE__ ) ;
00133     }
00134     
00135     _connected = false;
00136     int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
00137     
00138     if( descript == -1 ) 
00139     {
00140         string err("getting socket descriptor: ");
00141         const char* error_info = strerror(errno);
00142         if(error_info)
00143             err += (string)error_info;
00144         throw SocketException( err, __FILE__, __LINE__ ) ;
00145     } else {
00146         long holder;
00147         _socket = descript;
00148 
00149         //set socket to non-blocking mode
00150         holder = fcntl(_socket, F_GETFL, NULL);
00151         holder = holder | O_NONBLOCK;
00152         fcntl(_socket, F_SETFL, holder);
00153       
00154         int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
00155 
00156       
00157         if( res == -1 ) 
00158         {
00159             if(errno == EINPROGRESS) {
00160           
00161                 fd_set write_fd ;
00162                 struct timeval timeout ;
00163                 int maxfd = _socket;
00164           
00165                 timeout.tv_sec = 5;
00166                 timeout.tv_usec = 0;
00167           
00168                 FD_ZERO( &write_fd);
00169                 FD_SET( _socket, &write_fd );
00170           
00171                 if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
00172           
00173                     //reset socket to blocking mode
00174                     holder = fcntl(_socket, F_GETFL, NULL);
00175                     holder = holder & (~O_NONBLOCK);
00176                     fcntl(_socket, F_SETFL, holder);
00177             
00178                     //throw error - select could not resolve socket
00179                     string err( "selecting sockets: " ) ;
00180                     const char *error_info = strerror( errno ) ;
00181                     if( error_info )
00182                         err += (string)error_info ;
00183                     throw SocketException( err, __FILE__, __LINE__ ) ;
00184 
00185                 } 
00186                 else 
00187                 {
00188 
00189                     //check socket status
00190                     socklen_t lon;
00191                     int valopt;
00192                     lon = sizeof(int);
00193                     getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
00194             
00195                     if(valopt) 
00196                     {
00197 
00198                         //reset socket to blocking mode
00199                         holder = fcntl(_socket, F_GETFL, NULL);
00200                         holder = holder & (~O_NONBLOCK);
00201                         fcntl(_socket, F_SETFL, holder);
00202               
00203                         //throw error - did not successfully connect
00204                         string err("Did not successfully connect to server\n");
00205                         err += "Server may be down or you may be trying on the wrong port";
00206                         throw SocketException( err, __FILE__, __LINE__ ) ;
00207               
00208                     } 
00209                     else 
00210                     {
00211                         //reset socket to blocking mode
00212                         holder = fcntl(_socket, F_GETFL, NULL);
00213                         holder = holder & (~O_NONBLOCK);
00214                         fcntl(_socket, F_SETFL, holder);
00215               
00216                         //succesful connetion to server
00217                         _connected = true;
00218                     }
00219                 }
00220             } 
00221             else 
00222             {
00223 
00224                 //reset socket to blocking mode
00225                 holder = fcntl(_socket, F_GETFL, NULL);
00226                 holder = holder & (~O_NONBLOCK);
00227                 fcntl(_socket, F_SETFL, holder);
00228           
00229                 //throw error - errno was not EINPROGRESS
00230                 string err("socket connect: ");
00231                 const char* error_info = strerror(errno);
00232                 if(error_info)
00233                     err += (string)error_info;
00234                 throw SocketException( err, __FILE__, __LINE__ ) ;
00235             }
00236         }
00237         else
00238         {
00239             // The socket connect request completed immediately
00240             // even that the socket was in non-blocking mode
00241             
00242             //reset socket to blocking mode
00243             holder = fcntl(_socket, F_GETFL, NULL);
00244             holder = holder & (~O_NONBLOCK);
00245             fcntl(_socket, F_SETFL, holder);
00246             _connected = true;
00247         }
00248         
00249     }
00250 }
00251 
00252 void
00253 TcpSocket::listen()
00254 {
00255     if( _connected )
00256     {
00257         string err( "Socket is already connected" ) ;
00258         throw SocketException( err, __FILE__, __LINE__ ) ;
00259     }
00260 
00261     if( _listening )
00262     {
00263         string err( "Socket is already listening" ) ;
00264         throw SocketException( err, __FILE__, __LINE__ ) ;
00265     }
00266 
00267     int on = 1 ;
00268     struct sockaddr_in server ;
00269     server.sin_family = AF_INET ;
00270     server.sin_addr.s_addr = INADDR_ANY ;
00271     struct servent *sir = 0 ;
00272     sir = getservbyport( _portVal, "tcp" ) ;
00273     if( sir )
00274     {
00275         string error = sir->s_name + (string)" is using my socket" ;
00276         throw SocketException( error, __FILE__, __LINE__ ) ;
00277     }
00278     server.sin_port = htons( _portVal ) ;
00279     _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
00280     if( _socket != -1 )
00281     {
00282         if( !setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
00283                          (char*)&on, sizeof( on ) ) )
00284         {
00285             if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
00286             {
00287                 int length = sizeof( server ) ;
00288 #ifdef _GETSOCKNAME_USES_SOCKLEN_T      
00289                 if( getsockname( _socket, (struct sockaddr *)&server,
00290                                  (socklen_t *)&length ) == -1 )
00291 #else
00292                     if( getsockname( _socket, (struct sockaddr *)&server,
00293                                      &length ) == -1 )
00294 #endif
00295                     {
00296                         string error( "getting socket name" ) ;
00297                         const char* error_info = strerror( errno ) ;
00298                         if( error_info )
00299                             error += " " + (string)error_info ;
00300                         throw SocketException( error, __FILE__, __LINE__ ) ;
00301                     }
00302                 if( ::listen( _socket, 5 ) == 0 )
00303                 {
00304                     _listening = true ;
00305                 }
00306                 else
00307                 {
00308                     string error( "could not listen TCP socket" ) ;
00309                     const char* error_info = strerror( errno ) ;
00310                     if( error_info )
00311                         error += " " + (string)error_info ;
00312                     throw SocketException( error, __FILE__, __LINE__ ) ;
00313                 }
00314             }
00315             else
00316             {
00317                 string error( "could not bind TCP socket" ) ;
00318                 const char* error_info = strerror( errno ) ;
00319                 if( error_info )
00320                     error += " " + (string)error_info ;
00321                 throw SocketException( error, __FILE__, __LINE__ ) ;
00322             }
00323         }
00324         else
00325         {
00326             string error( "could not set SO_REUSEADDR on TCP socket" ) ;
00327             const char* error_info = strerror( errno ) ;
00328             if( error_info )
00329                 error += " " + (string)error_info ;
00330             throw SocketException( error, __FILE__, __LINE__ ) ;
00331         }
00332     }
00333     else
00334     {
00335         string error( "could not create socket" ) ;
00336         const char *error_info = strerror( errno ) ;
00337         if( error_info )
00338             error += " " + (string)error_info ;
00339         throw SocketException( error, __FILE__, __LINE__ ) ;
00340     }
00341 }
00342 
00349 void
00350 TcpSocket::dump( ostream &strm ) const
00351 {
00352     strm << BESIndent::LMarg << "TcpSocket::dump - ("
00353          << (void *)this << ")" << endl ;
00354     BESIndent::Indent() ;
00355     strm << BESIndent::LMarg << "host: " << _host << endl ;
00356     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00357     Socket::dump( strm ) ;
00358     BESIndent::UnIndent() ;
00359 }
00360 

Generated on Wed Jan 2 06:01:19 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.4