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     
00130     _connected = false;
00131     int descript = socket( AF_INET, SOCK_STREAM, pProtoEnt->p_proto ) ;
00132     
00133     if( descript == -1 ) 
00134     {
00135         string err("getting socket descriptor: ");
00136         const char* error_info = strerror(errno);
00137         if(error_info)
00138             err += (string)error_info;
00139         throw SocketException( err, __FILE__, __LINE__ ) ;
00140     } else {
00141         long holder;
00142         _socket = descript;
00143 
00144         //set socket to non-blocking mode
00145         holder = fcntl(_socket, F_GETFL, NULL);
00146         holder = holder | O_NONBLOCK;
00147         fcntl(_socket, F_SETFL, holder);
00148       
00149         int res = ::connect( descript, (struct sockaddr*)&sin, sizeof( sin ) );
00150 
00151       
00152         if( res == -1 ) 
00153         {
00154             if(errno == EINPROGRESS) {
00155           
00156                 fd_set write_fd ;
00157                 struct timeval timeout ;
00158                 int maxfd = _socket;
00159           
00160                 timeout.tv_sec = 5;
00161                 timeout.tv_usec = 0;
00162           
00163                 FD_ZERO( &write_fd);
00164                 FD_SET( _socket, &write_fd );
00165           
00166                 if( select( maxfd+1, NULL, &write_fd, NULL, &timeout) < 0 ) {
00167           
00168                     //reset socket to blocking mode
00169                     holder = fcntl(_socket, F_GETFL, NULL);
00170                     holder = holder & (~O_NONBLOCK);
00171                     fcntl(_socket, F_SETFL, holder);
00172             
00173                     //throw error - select could not resolve socket
00174                     string err( "selecting sockets: " ) ;
00175                     const char *error_info = strerror( errno ) ;
00176                     if( error_info )
00177                         err += (string)error_info ;
00178                     throw SocketException( err, __FILE__, __LINE__ ) ;
00179 
00180                 } 
00181                 else 
00182                 {
00183 
00184                     //check socket status
00185                     socklen_t lon;
00186                     int valopt;
00187                     lon = sizeof(int);
00188                     getsockopt(_socket, SOL_SOCKET, SO_ERROR, (void*) &valopt, &lon);
00189             
00190                     if(valopt) 
00191                     {
00192 
00193                         //reset socket to blocking mode
00194                         holder = fcntl(_socket, F_GETFL, NULL);
00195                         holder = holder & (~O_NONBLOCK);
00196                         fcntl(_socket, F_SETFL, holder);
00197               
00198                         //throw error - did not successfully connect
00199                         string err("Did not successfully connect to server\n");
00200                         err += "Server may be down or you may be trying on the wrong port";
00201                         throw SocketException( err, __FILE__, __LINE__ ) ;
00202               
00203                     } 
00204                     else 
00205                     {
00206                         //reset socket to blocking mode
00207                         holder = fcntl(_socket, F_GETFL, NULL);
00208                         holder = holder & (~O_NONBLOCK);
00209                         fcntl(_socket, F_SETFL, holder);
00210               
00211                         //succesful connetion to server
00212                         _connected = true;
00213                     }
00214                 }
00215             } 
00216             else 
00217             {
00218 
00219                 //reset socket to blocking mode
00220                 holder = fcntl(_socket, F_GETFL, NULL);
00221                 holder = holder & (~O_NONBLOCK);
00222                 fcntl(_socket, F_SETFL, holder);
00223           
00224                 //throw error - errno was not EINPROGRESS
00225                 string err("socket connect: ");
00226                 const char* error_info = strerror(errno);
00227                 if(error_info)
00228                     err += (string)error_info;
00229                 throw SocketException( err, __FILE__, __LINE__ ) ;
00230             }
00231         }
00232         else
00233         {
00234             // The socket connect request completed immediately
00235             // even that the socket was in non-blocking mode
00236             
00237             //reset socket to blocking mode
00238             holder = fcntl(_socket, F_GETFL, NULL);
00239             holder = holder & (~O_NONBLOCK);
00240             fcntl(_socket, F_SETFL, holder);
00241             _connected = true;
00242         }
00243         
00244     }
00245 }
00246 
00247 void
00248 TcpSocket::listen()
00249 {
00250     if( _connected )
00251     {
00252         string err( "Socket is already connected" ) ;
00253         throw SocketException( err, __FILE__, __LINE__ ) ;
00254     }
00255 
00256     if( _listening )
00257     {
00258         string err( "Socket is already listening" ) ;
00259         throw SocketException( err, __FILE__, __LINE__ ) ;
00260     }
00261 
00262     int on = 1 ;
00263     struct sockaddr_in server ;
00264     server.sin_family = AF_INET ;
00265     server.sin_addr.s_addr = INADDR_ANY ;
00266     struct servent *sir = 0 ;
00267     sir = getservbyport( _portVal, "tcp" ) ;
00268     if( sir )
00269     {
00270         string error = sir->s_name + (string)" is using my socket" ;
00271         throw SocketException( error, __FILE__, __LINE__ ) ;
00272     }
00273     server.sin_port = htons( _portVal ) ;
00274     _socket = socket( AF_INET, SOCK_STREAM, 0 ) ;
00275     if( _socket != -1 )
00276     {
00277         if( !setsockopt( _socket, SOL_SOCKET, SO_REUSEADDR,
00278                          (char*)&on, sizeof( on ) ) )
00279         {
00280             if( bind( _socket, (struct sockaddr*)&server, sizeof server) != -1 )
00281             {
00282                 int length = sizeof( server ) ;
00283 #ifdef _GETSOCKNAME_USES_SOCKLEN_T      
00284                 if( getsockname( _socket, (struct sockaddr *)&server,
00285                                  (socklen_t *)&length ) == -1 )
00286 #else
00287                     if( getsockname( _socket, (struct sockaddr *)&server,
00288                                      &length ) == -1 )
00289 #endif
00290                     {
00291                         string error( "getting socket name" ) ;
00292                         const char* error_info = strerror( errno ) ;
00293                         if( error_info )
00294                             error += " " + (string)error_info ;
00295                         throw SocketException( error, __FILE__, __LINE__ ) ;
00296                     }
00297                 if( ::listen( _socket, 5 ) == 0 )
00298                 {
00299                     _listening = true ;
00300                 }
00301                 else
00302                 {
00303                     string error( "could not listen TCP socket" ) ;
00304                     const char* error_info = strerror( errno ) ;
00305                     if( error_info )
00306                         error += " " + (string)error_info ;
00307                     throw SocketException( error, __FILE__, __LINE__ ) ;
00308                 }
00309             }
00310             else
00311             {
00312                 string error( "could not bind TCP socket" ) ;
00313                 const char* error_info = strerror( errno ) ;
00314                 if( error_info )
00315                     error += " " + (string)error_info ;
00316                 throw SocketException( error, __FILE__, __LINE__ ) ;
00317             }
00318         }
00319         else
00320         {
00321             string error( "could not set SO_REUSEADDR on TCP socket" ) ;
00322             const char* error_info = strerror( errno ) ;
00323             if( error_info )
00324                 error += " " + (string)error_info ;
00325             throw SocketException( error, __FILE__, __LINE__ ) ;
00326         }
00327     }
00328     else
00329     {
00330         string error( "could not create socket" ) ;
00331         const char *error_info = strerror( errno ) ;
00332         if( error_info )
00333             error += " " + (string)error_info ;
00334         throw SocketException( error, __FILE__, __LINE__ ) ;
00335     }
00336 }
00337 
00344 void
00345 TcpSocket::dump( ostream &strm ) const
00346 {
00347     strm << BESIndent::LMarg << "TcpSocket::dump - ("
00348          << (void *)this << ")" << endl ;
00349     BESIndent::Indent() ;
00350     strm << BESIndent::LMarg << "host: " << _host << endl ;
00351     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00352     Socket::dump( strm ) ;
00353     BESIndent::UnIndent() ;
00354 }
00355 

Generated on Wed Aug 29 02:59:02 2007 for OPeNDAP Back End Server (BES) by  doxygen 1.5.2