S3FC project page S3FC home page

Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

s3_socket_tcp.cc

Go to the documentation of this file.
00001 /*
00002  * Stone Three Foundation Class (s3fc) provides a number of utility classes.
00003  * Copyright (C) 2001 by Stone Three Signal Processing (Pty) Ltd.
00004  *
00005  * Authored by Stone Three Signal Processing (Pty) Ltd.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  * 
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  * 
00021  * Please see the file 'COPYING' in the source root directory.
00022  */
00023 
00033 #include <s3fc/s3_socket_tcp.h>
00034 #include <s3fc/s3_thread_base.h>
00035 #include <s3fc/s3_macros.h>
00036 #include <sstream>
00037 #ifndef _WIN32
00038 #include <sys/time.h>
00039 #endif
00040 
00041 s3_socket_tcp::s3_socket_tcp(int newsock, bool new_set_reuseaddr)
00042    throw(s3_generic_exception) :
00043    set_reuseaddr(new_set_reuseaddr), my_errno(0), my_h_errno(0)
00044 {
00045    if(newsock == INVALID_SOCKET)
00046    {
00047       if ( !initialize() )
00048       {
00049     throw s3_generic_exception("s3_socket_tcp::s3_socket_tcp()",
00050                 "Could not initialize socket");
00051       }
00052    }
00053    else
00054    {
00055       sock = newsock;
00056    }
00057 }
00058 
00059 s3_socket_tcp::s3_socket_tcp(const s3_socket_tcp& template_sock)
00060    throw(s3_generic_exception) :
00061    set_reuseaddr(template_sock.set_reuseaddr), my_errno(0), my_h_errno(0)
00062 {
00063    if ( !initialize() )
00064    {
00065       throw s3_generic_exception("s3_socket_tcp::s3_socket_tcp()",
00066              "Could not initialize socket");
00067    }
00068 }
00069 
00070 s3_socket_tcp::~s3_socket_tcp()
00071 {
00072    close(false); //don't reinitialize
00073 }
00074 
00075 bool s3_socket_tcp::initialize()
00076 {
00077    set_errnos(); //reset errors here in case we miss a return
00078    if( (sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == 1)
00079    {
00080       S3FC_DBG2_("s3_socket_tcp::initialize()", "::socket() failed");
00081       sock = INVALID_SOCKET;
00082       set_errnos(errno);
00083       return false; //don't set socket options
00084    }
00085    S3FC_DBG2_("s3_socket_tcp::initialize()", "::socket() ok");
00086    
00087    // SO_REUSEADDR gets rid of timeout after socket use.
00088    int optval = 1;
00089    if( set_reuseaddr )
00090    {
00091       if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval,
00092            sizeof(int)) != 0 )
00093       {
00094     set_errnos(errno);
00095     return false;
00096       }
00097    }
00098    return true;
00099 }
00100 
00101 void s3_socket_tcp::set_errnos(int new_errno, int new_h_errno)
00102 {
00103    my_errno = new_errno;
00104    my_h_errno = new_h_errno;
00105 }
00106 
00107 void s3_socket_tcp::change_socket(int newsock)
00108 {
00109    close(false);
00110    sock = newsock;
00111 }
00112 
00113 bool s3_socket_tcp::bind(in_addr_t addr, in_port_t port)
00114 {
00115    S3FC_DBG_("s3_socket_tcp::bind(addr,port="<< port << ")");
00116    set_errnos();   
00117    sockaddr_in my_addr;
00118    my_addr.sin_family = AF_INET;
00119    my_addr.sin_addr.s_addr = htonl(addr);
00120    my_addr.sin_port = htons(port);
00121    if ( ::bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) != 0 )
00122    {
00123       S3FC_DBG2_("s3_socket_tcp::bind()", "::bind() failed");
00124       set_errnos(errno);
00125       return false;
00126    }
00127    S3FC_DBG2_("s3_socket_tcp::bind()", "::bind() ok");
00128    return true;   
00129 }
00130 
00131 bool s3_socket_tcp::listen(int backlog)
00132 {
00133    S3FC_DBG_( "s3_socket_tcp::listen(backlog=" << backlog << ")");
00134    set_errnos();   
00135    if( ::listen(sock, backlog) != 0 )
00136    {
00137       S3FC_DBG2_("s3_socket_tcp::listen()", "::listen() failed");
00138       set_errnos(errno);
00139       return false;
00140    }
00141    S3FC_DBG2_("s3_socket_tcp::listen()", "::listen() ok");
00142    return true;   
00143 }
00144 
00145 bool s3_socket_tcp::accept(s3_socket_tcp& newsock, sockaddr_in* clientname)
00146 {
00147    S3FC_DBG_("s3_socket_tcp::accept()");
00148    set_errnos();
00149    int accepted_socket;
00150 #ifdef _WIN32
00151    int size;
00152 #else
00153    socklen_t size;
00154 #endif
00155    size = clientname != 0 ? sizeof(*clientname) : 0;
00156    if( (accepted_socket =
00157    ::accept(sock, (struct sockaddr*)&clientname, &size)) == -1)
00158    {
00159       S3FC_DBG2_( "s3_socket_tcp::accept()", "::accept() failed" );
00160       set_errnos(errno);
00161       return false;
00162    }
00163    S3FC_DBG2_("s3_socket_tcp::accept()", "::accept() ok");
00164    newsock.change_socket(accepted_socket);
00165    return true;   
00166 }
00167 
00168 bool s3_socket_tcp::connect(const std::string& IP_address, in_port_t port)
00169 {
00170    S3FC_DBG_( "s3_socket_tcp::connect(IP_address=" << IP_address
00171          << ",port=" << port << ")" );
00172    
00173    set_errnos();   
00174    hostent *he = gethostbyname(IP_address.c_str());
00175    if (he == 0)
00176    {
00177       S3FC_DBG2_("s3_socket_tcp::connect()", "gethostbyname failed");
00178       if ( h_errno >= 0 )
00179       {
00180     set_errnos(-1, h_errno);
00181       }
00182       else
00183       {
00184     set_errnos(errno, 0);
00185       }
00186       return false;
00187    }
00188    
00189    sockaddr_in addr;
00190    memset(&addr, 0, sizeof(addr));
00191    memcpy(&addr.sin_addr, he->h_addr, he->h_length);
00192    addr.sin_port = htons(port);
00193    addr.sin_family = he->h_addrtype;
00194    
00195    if ( ::connect(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0 )
00196    {
00197       S3FC_DBG2_("s3_socket_tcp::connect()", "::connect() failed");
00198       set_errnos(errno);
00199       return false;
00200    }
00201    S3FC_DBG2_("s3_socket_tcp::connect()", "::connect() ok");
00202    return true;   
00203 }
00204 
00205 bool s3_socket_tcp::close(bool reinit) throw(s3_generic_exception)
00206 {
00207    S3FC_DBG_("s3_socket_tcp::close()");
00208    
00209    set_errnos();
00210    int ret = 0;
00211    if ( is_valid() )
00212    {
00213       S3FC_DBG2_("s3_socket_tcp::close()", "closing socket");
00214 #ifdef _WIN32
00215       ret = closesocket(sock);
00216 #else
00217       ret = ::close(sock);
00218 #endif
00219    }
00220    sock = INVALID_SOCKET;
00221    if ( ret != 0 )
00222    {
00223       S3FC_DBG2_("s3_socket_tcp::close()", "::close() failed");
00224       set_errnos(errno);
00225       return false;
00226    }
00227    S3FC_DBG2_("s3_socket_tcp::close()", "::close() ok");
00228    if ( reinit && !initialize() ) //for listen() or connect() after close
00229    {
00230       throw s3_generic_exception("s3_socket_tcp::close()",
00231              "Could not reinitialize socket");
00232    }
00233    return true;
00234 }
00235 
00236 std::string s3_socket_tcp::get_error() const
00237 {
00238    S3FC_DBG2_( "s3_socket_tcp::get_error()", "my_errno = " << my_errno
00239           << ", " << "my_h_errno = " << my_h_errno );
00240 
00241    std::string error_string;
00242    if ( my_errno >= 0 )
00243    {
00244 #ifdef _WIN32
00245       std::stringstream ss;
00246       ss << "WSAGetLastError: " << my_errno;
00247       error_string = ss.str();
00248 #else
00249       error_string = strerror(my_errno);
00250 #endif
00251    }
00252    else if ( my_h_errno > 0 )
00253    {
00254 #ifdef _WIN32
00255       std::stringstream ss;
00256       ss << "WSAGetLastError: (h) " << my_h_errno;
00257       error_string = ss.str();
00258 #else
00259       error_string = hstrerror(my_h_errno);
00260 #endif
00261    }   
00262    else if ( my_errno == -1 )
00263    {
00264       error_string = "Connection closed";
00265    }
00266    else if ( my_errno == -2 )
00267    {
00268       error_string = "Termination semaphore posted";
00269    }
00270    else
00271    {
00272       error_string = "Unknown error";
00273    }
00274 
00275    S3FC_DBG_("error_string = " << error_string);
00276 
00277    return error_string;
00278 }
00279 
00280 bool s3_socket_tcp::read(void *data, int size, s3_semaphore *term)
00281 {
00282    S3FC_DBG2_("s3_socket_tcp::read()", "socket = " << sock);
00283 
00284    set_errnos();
00285    int rem_size = size;
00286    int e = 0;
00287    // changed unsigned char* to char* for WIN32 recv [fswane].
00288    char *buf = reinterpret_cast<char*>(data);
00289    do
00290    {
00291       // If a thread canceller is available, call it.
00292       s3_thread_base::test_cancel();
00293       
00294       // Test cancellation of the read via semaphore.
00295       if (term != 0 && term->try_wait())
00296       {
00297     set_errnos(-2);
00298     return false;
00299       }
00300       
00301       switch (select_rd(1) )
00302       {
00303       case -1 : return false; // errno already set
00304       case  0 : continue;     // Just a timeout so carry on
00305       case  1 : break;        // I smell data !!
00306       default: throw s3_generic_exception("s3_socket_tcp::read()",
00307                  "select_rd() returned an invalid value");
00308       }
00309       
00310       errno = 0;
00311 #ifdef _WIN32
00312       int ret = recv(sock, buf, rem_size, 0);
00313       /*
00314        * Checkking for errors and if the remote socket disconnected.
00315        * The logic is as follow:
00316        *  - The return value will be zero if the remote socket disconnected.
00317        *  - The return value will be SOCKET_ERROR if some error condition 
00318        *    occured.
00319        *  - Otherwise ther return value indicates how many bytes were read.
00320        *
00321        * 30 May 2002: Jan Pool <jpool@stonethree.com>
00322        */
00323       if ( ( ret == 0 ) || ( ret == SOCKET_ERROR ) )
00324       {
00325     if ( ret == SOCKET_ERROR )
00326     {
00327        // Get error number.
00328        e = WSAGetLastError();
00329        if (e == WSAEWOULDBLOCK) 
00330        {
00331           /*
00332       * No data: select probably timed out, but since we are in 
00333       * non-blocking mode the resv fall through.
00334       * This error code saves the day.
00335       */
00336           continue;
00337        } 
00338        else if (e == WSAENOTCONN)
00339        {
00340           // This indicates that the socket is not connected
00341           // I think it means the socket was never connected <jp>
00342           set_errnos(-1);
00343        }
00344        else
00345        {
00346           set_errnos(e);
00347        }
00348     }
00349     else
00350     {
00351        set_errnos(-1); // Remote socket disconnected.
00352     }
00353     return false;
00354       }
00355 #else
00356       int ret = recv(sock, buf, rem_size, MSG_DONTWAIT);
00357       e = errno;
00358       // loop if no data available
00359       if (e == EAGAIN) 
00360       {
00361     continue;
00362       }
00363       else if ( (ret <= 0 ) || (e != 0) ) // Some other error...
00364       {
00365          // ugly, but I cannot find another way to see
00366          // if the connection was closed by peer !!FIXME
00367          if (e == 0)
00368     {
00369        set_errnos(-1);
00370     }
00371     else
00372     {
00373        set_errnos(e);
00374     }
00375     return false;  
00376       }
00377 #endif
00378       // if we can assume that no error has occured [goof]
00379       // Account for new data and update pointers
00380       rem_size -= ret;  // Remaining data to acquire
00381       buf = buf + ret; // update pointer into buffer
00382       
00383       // removed some #if 0'ed code here [albert]
00384    }
00385    while (rem_size > 0);
00386    
00387    return true;
00388 }
00389 
00390 bool s3_socket_tcp::write(void *data, int size, int max_packet,
00391            s3_semaphore *term)
00392 {
00393    S3FC_DBG_("s3_socket_tcp::write() called");
00394 
00395    set_errnos();   
00396    int rem_size = size;
00397    int packet_size = size;
00398    int e = 0;
00399    // changed unsigned char* to char* for WIN32 recv [fswane].
00400    char *buf = (char *) data;
00401 
00402    if (max_packet > size )
00403    {
00404       packet_size = max_packet; //was packet_size = size
00405    }
00406    // Get all the data out the door
00407    do
00408    {
00409       s3_thread_base::test_cancel();
00410       
00411       // Test cancellation of the read via semaphore.
00412       if (term != 0 && term->try_wait())
00413       {
00414     set_errnos(-2);
00415     return false;
00416       }
00417 
00418       switch (select_wr(1) )
00419       {
00420       case -1 : return false; // errno already set
00421       case  0 : continue;     // Just a timeout so carry on
00422       case  1 : break;        // I smell data !!
00423       default: throw s3_generic_exception("s3_socket_tcp::write()",
00424                  "select_wr() returned an invalid value");
00425       }
00426     
00427       errno = 0;
00428 #ifdef _WIN32
00429       int ret = send(sock, buf, packet_size, 0);
00430       if (ret == SOCKET_ERROR) e = WSAGetLastError();
00431       // loop if no data available
00432       if (e == WSAEWOULDBLOCK) 
00433       {
00434     continue;
00435       } 
00436       else if (ret == SOCKET_ERROR)
00437       {
00438     set_errnos(e);
00439     return false;
00440       }
00441 #else
00442       int ret = send(sock, buf, packet_size, MSG_DONTWAIT | MSG_NOSIGNAL);
00443       e = errno;
00444       if ( e == EAGAIN)
00445       {
00446          continue;  // just nothing available
00447       }
00448       else if ( (ret < 0 ) || (e != 0) ) // Some other error...
00449       {
00450          // Something we cannot handle has happened
00451     set_errnos(e);
00452     return false;
00453       }
00454 #endif
00455       // if we get here, we can assume that no error has occured [goof]
00456       // Update our counters for the data we have received
00457       rem_size = rem_size - ret;  // Remaining data to acquire
00458       buf = buf + ret; // update pointer into buffer
00459       if (rem_size < packet_size) packet_size = rem_size;
00460       
00461       //removed some #if 0'ed code [albert]
00462    }
00463    while (rem_size > 0);
00464 
00465    return true;
00466 }
00467 
00468 int s3_socket_tcp::select_rd(int delay)
00469 {
00470    fd_set fds, fde;
00471    timeval tv;
00472    // Set select to timeout after a second.
00473    tv.tv_sec = delay;
00474    tv.tv_usec = 0;
00475    // Set up the FD and timeout for select
00476    FD_ZERO(&fds);
00477    FD_ZERO(&fde);
00478    FD_SET(sock, &fds);
00479    FD_SET(sock, &fde);
00480    errno = 0;
00481    int ret = select(sock+1, &fds, 0, &fde, &tv);
00482    if ( ( ret == -1 ) ||  FD_ISSET(sock, &fde) )
00483    {
00484       set_errnos(errno);
00485       return -1;
00486    }
00487    if (ret == 0 ) // Nothing happened so return timeout
00488    {
00489       return 0;
00490    }
00491    if ( FD_ISSET(sock, &fds) ) // Data available for read on socket
00492    {
00493       return 1;
00494    }
00495    // Shuts compiler up. This is an impossible return
00496    return -2;
00497 }
00498 
00499 int s3_socket_tcp::select_wr(int delay)
00500 {
00501    fd_set fds, fde;
00502    timeval tv;
00503    // Set select to timeout after a second.
00504    tv.tv_sec = delay;
00505    tv.tv_usec = 0;
00506    // Set up the FD and timeout for select
00507    FD_ZERO(&fds);
00508    FD_ZERO(&fde);
00509    FD_SET(sock, &fds);
00510    FD_SET(sock, &fde);
00511    errno = 0;
00512    int ret = select(sock+1, 0, &fds, &fde, &tv);
00513    if ( ( ret == -1 ) ||  FD_ISSET(sock, &fde) )
00514    {
00515       set_errnos(errno);
00516       return -1;
00517    }
00518    if (ret == 0 ) // Nothing happened so return timeout
00519    {
00520       return 0;
00521    }
00522    if ( FD_ISSET(sock, &fds) ) // Data available for read on socket
00523    {
00524       return 1;
00525    }
00526    // Shuts compiler up. This is an impossible return
00527    return -2;
00528    
00529 }
00530 

Send comments to: s3fc@stonethree.com SourceForge Logo