]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/sg_socket.cxx
Bernie Bright:
[simgear.git] / simgear / io / sg_socket.cxx
index 43735428de4ed348ce07519ac223f30f57e4eaad..b348ddf19f98cae3c862cce8e328044b4adbb4ec 100644 (file)
@@ -1,6 +1,7 @@
 // sg_socket.cxx -- Socket I/O routines
 //
 // Written by Curtis Olson, started November 1999.
+// Modified by Bernie Bright <bbright@bigpond.net.au>, May 2002.
 //
 // Copyright (C) 1999  Curtis L. Olson - curt@flightgear.org
 //
 
 #include <simgear/compiler.h>
 
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#  include <sys/time.h>                // select()
-#  include <sys/types.h>       // socket(), bind(), select(), accept()
-#  include <sys/socket.h>      // socket(), bind(), listen(), accept()
-#  include <netinet/in.h>      // struct sockaddr_in
-#  include <netdb.h>           // gethostbyname()
-#  include <unistd.h>          // select(), fsync()/fdatasync(), fcntl()
-#  include <fcntl.h>           // fcntl()
-#endif
-
 #if defined( sgi )
 #include <strings.h>
 #endif
 
 #include "sg_socket.hxx"
 
+bool SGSocket::init = false;
 
-SGSocket::SGSocket( const string& host, const string& port, 
+SGSocket::SGSocket( const string& host, const string& port_
                    const string& style ) :
     hostname(host),
-    port_str(port),
-    save_len(0)
+    port_str(port_),
+    save_len(0),
+    client(0),
+    is_tcp(false),
+    is_server(false),
+    first_read(false)
 {
-#if defined(_MSC_VER) || defined(__MINGW32__)
-    if (!wsock_init && !wsastartup()) {
-       SG_LOG( SG_IO, SG_ALERT, "Winsock not available");
+    if (!init)
+    {
+       netInit();
+       init = true;
     }
-#endif
 
-    if ( style == "udp" ) {
-       sock_style = SOCK_DGRAM;
-    } else if ( style == "tcp" ) {
-       sock_style = SOCK_STREAM;
-    } else {
-       sock_style = SOCK_DGRAM;
+    if ( style == "tcp" )
+    {
+       is_tcp = true;
+    }
+    else if ( style != "udp" )
+    {
        SG_LOG( SG_IO, SG_ALERT,
                "Error: SGSocket() unknown style = " << style );
     }
@@ -68,126 +64,65 @@ SGSocket::SGSocket( const string& host, const string& port,
 }
 
 
-SGSocket::~SGSocket() {
+SGSocket::~SGSocket()
+{
+    this->close();
 }
 
 
-SGSocket::SocketType SGSocket::make_server_socket () {
-    struct sockaddr_in name;
-
-#if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER ) || defined(__MINGW32__) || defined( __APPLE__ )
-    int length;
-#else
-    socklen_t length;
-#endif
-     
-    // Create the socket.
-    sock = socket (PF_INET, sock_style, 0);
-    if (sock == INVALID_SOCKET) {
+bool
+SGSocket::make_server_socket()
+{
+    if (!sock.open( is_tcp ))
+    {
         SG_LOG( SG_IO, SG_ALERT, 
                 "Error: socket() failed in make_server_socket()" );
-        return INVALID_SOCKET;
-    }
-     
-    // Give the socket a name.
-    name.sin_family = AF_INET;
-    name.sin_addr.s_addr = INADDR_ANY;
-    name.sin_port = htons(port); // set port to zero to let system pick
-    name.sin_addr.s_addr = htonl (INADDR_ANY);
-    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) != 0) {
-           SG_LOG( SG_IO, SG_ALERT,
-                   "Error: bind() failed in make_server_socket()" );
-        return INVALID_SOCKET;
+       return false;
     }
 
-    // Find the assigned port number
-    length = sizeof(struct sockaddr_in);
-    if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
+    if (sock.bind( "", port ) < 0)
+    {
        SG_LOG( SG_IO, SG_ALERT,
-               "Error: getsockname() failed in make_server_socket()" );
-        return INVALID_SOCKET;
+               "Error: bind() failed in make_server_socket()" );
+       sock.close();
+        return false;
     }
-    port = ntohs(name.sin_port);
 
-    return sock;
+    return true;
 }
 
 
-SGSocket::SocketType SGSocket::make_client_socket () {
-    struct sockaddr_in name;
-    struct hostent *hp;
-     
-    SG_LOG( SG_IO, SG_INFO, "Make client socket()" );
-
-    // Create the socket.
-    sock = socket (PF_INET, sock_style, 0);
-    if (sock == INVALID_SOCKET) {
+bool
+SGSocket::make_client_socket()
+{
+    if (!sock.open( is_tcp ))
+    {
         SG_LOG( SG_IO, SG_ALERT, 
-                "Error: socket() failed in make_server_socket()" );
-        return INVALID_SOCKET;
-    }
-     
-    // specify address family
-    name.sin_family = AF_INET;
-
-    // get the hosts official name/info
-    hp = gethostbyname( hostname.c_str() );
-    if (hp == NULL) {
-       SG_LOG( SG_IO, SG_ALERT, "Error: hostname lookup failed" );
-       return INVALID_SOCKET;
+                "Error: socket() failed in make_client_socket()" );
+       return false;
     }
 
-    // Connect this socket to the host and the port specified on the
-    // command line
-#if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
-    bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
-#else
-    bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
-#endif
-    name.sin_port = htons(port);
-
-    if ( connect(sock, (struct sockaddr *) &name, 
-                sizeof(struct sockaddr_in)) != 0 )
+    if (sock.connect( hostname.c_str(), port ) < 0)
     {
-       closesocket(sock);
        SG_LOG( SG_IO, SG_ALERT, 
                "Error: connect() failed in make_client_socket()" );
-       return INVALID_SOCKET;
+       sock.close();
+        return false;
     }
 
-    return sock;
-}
-
-
-// Wrapper functions
-int SGSocket::readsocket( int fd, void *buf, size_t count ) {
-#if defined(_MSC_VER) || defined(__MINGW32__)
-    return ::recv( fd, (char *)buf, count, 0 );
-#else
-    return ::read( fd, buf, count );
-#endif
-}
-
-int SGSocket::writesocket( int fd, const void *buf, size_t count ) {
-#if defined(_MSC_VER) || defined(__MINGW32__)
-    return ::send( fd, (const char*)buf, count, 0 );
-#else
-    return ::write( fd, buf, count );
-#endif
-}
-
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-int SGSocket::closesocket( int fd ) {
-    return ::close( fd );
+    return true;
 }
-#endif
-
 
 // If specified as a server (in direction for now) open the master
 // listening socket.  If specified as a client (out direction), open a
 // connection to a server.
-bool SGSocket::open( const SGProtocolDir d ) {
-    set_dir( d );
+bool
+SGSocket::open( SGProtocolDir direction )
+{
+    set_dir( direction );
+
+    is_server = is_tcp &&
+       (direction == SG_IO_IN || direction == SG_IO_BI);
 
     if ( port_str == "" || port_str == "any" ) {
        port = 0; 
@@ -195,69 +130,71 @@ bool SGSocket::open( const SGProtocolDir d ) {
        port = atoi( port_str.c_str() );
     }
     
-    // client_connections.clear();
-
-    if ( get_dir() == SG_IO_IN ) {
+    if (direction == SG_IO_IN)
+    {
        // this means server for now
 
        // Setup socket to listen on.  Set "port" before making this
        // call.  A port of "0" indicates that we want to let the os
        // pick any available port.
-       sock = make_server_socket();
-       if ( sock == INVALID_SOCKET ) {
-           SG_LOG( SG_IO, SG_ALERT, "socket creation failed" );
+       if (!make_server_socket())
+       {
+           SG_LOG( SG_IO, SG_ALERT, "SG_IO_IN socket creation failed" );
            return false;
        }
 
-       SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
-
-       if ( sock_style == SOCK_DGRAM ) {
+       if ( !is_tcp )
+       {
            // Non-blocking UDP
            nonblock();
-       } else {
+       }
+       else
+       {
            // Blocking TCP
            // Specify the maximum length of the connection queue
-           listen( sock, SG_MAX_SOCKET_QUEUE );
+           sock.listen( SG_MAX_SOCKET_QUEUE );
        }
 
-    } else if ( get_dir() == SG_IO_OUT ) {
+    }
+    else if (direction == SG_IO_OUT)
+    {
        // this means client for now
 
-       sock = make_client_socket();
-       // TODO: check for error.
+       if (!make_client_socket())
+       {
+           SG_LOG( SG_IO, SG_ALERT, "SG_IO_OUT socket creation failed" );
+           return false;
+       }
 
-       if ( sock_style == SOCK_DGRAM ) {
+       if ( !is_tcp )
+       {
            // Non-blocking UDP
            nonblock();
        }
-    } else if ( get_dir() == SG_IO_BI && sock_style == SOCK_STREAM ) {
+    }
+    else if (direction == SG_IO_BI && is_tcp)
+    {
        // this means server for TCP sockets
 
        // Setup socket to listen on.  Set "port" before making this
        // call.  A port of "0" indicates that we want to let the os
        // pick any available port.
-       sock = make_server_socket();
-       // TODO: check for error.
-
-       SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
-
+       if (!make_server_socket())
+       {
+           SG_LOG( SG_IO, SG_ALERT, "SG_IO_BI socket creation failed" );
+           return false;
+       }
        // Blocking TCP
        // Specify the maximum length of the connection queue
-       listen( sock, SG_MAX_SOCKET_QUEUE );
-    } else {
+       sock.listen( SG_MAX_SOCKET_QUEUE );
+    }
+    else
+    {
        SG_LOG( SG_IO, SG_ALERT, 
                "Error:  bidirection mode not available for UDP sockets." );
        return false;
     }
 
-    if ( sock < 0 ) {
-       SG_LOG( SG_IO, SG_ALERT, "Error opening socket: " << hostname
-               << ":" << port );
-       return false;
-    }
-
-    // extra SOCK_STREAM stuff
-    msgsock = INVALID_SOCKET;
     first_read = false;
 
     return true;
@@ -266,40 +203,25 @@ bool SGSocket::open( const SGProtocolDir d ) {
 
 // read data from socket (server)
 // read a block of data of specified size
-int SGSocket::read( char *buf, int length ) {
-    if ( sock == INVALID_SOCKET ) {
+int
+SGSocket::read( char *buf, int length )
+{
+    if (sock.getHandle() == -1 &&
+       (client == 0 || client->getHandle() == -1))
+    {
        return 0;
     }
 
-    int result = 0;
-    // check for potential input
-    fd_set ready;
-    FD_ZERO(&ready);
-    FD_SET(sock, &ready);
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-
     // test for any input available on sock (returning immediately, even if
     // nothing)
-    select(32, &ready, 0, 0, &tv);
-
-    if ( FD_ISSET(sock, &ready) ) {
-       // cout << "data ready" << endl;
-
-       if ( sock_style == SOCK_STREAM ) {
-           if ( msgsock == INVALID_SOCKET ) {
-               msgsock = accept(sock, 0, 0);
-               closesocket(sock);
-               sock = msgsock;
-           } else {
-               result = readsocket( sock, buf, length );
-           }
-       } else {
-           result = readsocket( sock, buf, length );
-       }
+    int result = poll();
+
+    if (result > 0)
+    {
+       result = sock.recv( buf, length );
 
-       if ( result != length ) {
+       if ( result != length )
+       {
            SG_LOG( SG_IO, SG_INFO, 
                    "Warning: read() not enough bytes." );
        }
@@ -310,92 +232,74 @@ int SGSocket::read( char *buf, int length ) {
 
 
 // read a line of data, length is max size of input buffer
-int SGSocket::readline( char *buf, int length ) {
-    if ( sock == INVALID_SOCKET ) {
+int
+SGSocket::readline( char *buf, int length )
+{
+    if (sock.getHandle() == -1 &&
+       (client == 0 || client->getHandle() == -1))
+    {
        return 0;
     }
 
-    // cout << "sock = " << sock << endl;
-
-    // check for potential input
-    fd_set ready;
-    FD_ZERO(&ready);
-    FD_SET(sock, &ready);
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-
     // test for any input read on sock (returning immediately, even if
     // nothing)
-    int result = select(32, &ready, 0, 0, &tv);
-    // cout << "result = " << result << endl;
+    int result = this->poll();
 
-    if ( FD_ISSET(sock, &ready) ) {
-       // cout << "fd change state\n";
+    if (result > 0)
+    {
        // read a chunk, keep in the save buffer until we have the
        // requested amount read
 
-       if ( sock_style == SOCK_STREAM ) {
-           // cout << "sock_stream\n";
-           if ( msgsock == INVALID_SOCKET ) {
-               // cout << "msgsock == invalid\n";
-               msgsock = sock;
-               sock = accept(msgsock, 0, 0);
-           } else {
-               // cout << "ready to read\n";
-               char *buf_ptr = save_buf + save_len;
-               result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE
-                                    - save_len );
-               // cout << "read result = " << result << endl;
-
-               if ( result > 0 ) {
-                   first_read = true;
-               }
-
-               save_len += result;
-
-               // Try and detect that the remote end died.  This
-               // could cause problems so if you see connections
-               // dropping for unexplained reasons, LOOK HERE!
-               if ( result == 0 && save_len == 0 && first_read == true ) {
-                   SG_LOG( SG_IO, SG_ALERT, 
-                           "Connection closed by foreign host." );
-                    close();
-               }
+       if (is_tcp)
+       {
+           char *buf_ptr = save_buf + save_len;
+           result = client->recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
+               
+           if ( result > 0 )
+           {
+               first_read = true;
+           }
+
+           save_len += result;
+
+           // Try and detect that the remote end died.  This
+           // could cause problems so if you see connections
+           // dropping for unexplained reasons, LOOK HERE!
+           if (result == 0 && save_len == 0 && first_read == true)
+           {
+               SG_LOG( SG_IO, SG_ALERT, 
+                       "Connection closed by foreign host." );
+               delete client;
+               client = 0;
            }
-       } else {
+       }
+       else
+       {
            char *buf_ptr = save_buf + save_len;
-           result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
+           result = sock.recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
            save_len += result;
        }
-
-       // cout << "current read = " << buf_ptr << endl;
-       // cout << "current save_buf = " << save_buf << endl;
-       // cout << "save_len = " << save_len << endl;
-    } else {
-       // cout << "no data ready\n";
     }
 
     // look for the end of line in save_buf
     int i;
-    for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
+    for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i )
+       ;
     if ( save_buf[i] == '\n' ) {
        result = i + 1;
     } else {
        // no end of line yet
-       // cout << "no eol found" << endl;
        return 0;
     }
-    // cout << "line length = " << result << endl;
 
     // we found an end of line
 
     // copy to external buffer
     strncpy( buf, save_buf, result );
     buf[result] = '\0';
-    // cout << "sg_socket line = " << buf << endl;
-    
+
     // shift save buffer
+    //memmove( save_buf+, save_buf+, ? );
     for ( i = result; i < save_len; ++i ) {
        save_buf[ i - result ] = save_buf[i];
     }
@@ -406,66 +310,23 @@ int SGSocket::readline( char *buf, int length ) {
 
 
 // write data to socket (client)
-int SGSocket::write( const char *buf, const int length ) {
-    if ( sock == INVALID_SOCKET ) {
+int
+SGSocket::write( const char *buf, const int length )
+{
+    netSocket* s = client == 0 ? &sock : client;
+    if (s->getHandle() == -1)
+    {
        return 0;
     }
 
     bool error_condition = false;
 
-    if ( writesocket(sock, buf, length) < 0 ) {
+    if ( s->send( buf, length ) < 0 )
+    {
        SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
        error_condition = true;
     }
 
-#if 0 
-    // check for any new client connection requests
-    fd_set ready;
-    FD_ZERO(&ready);
-    FD_SET(sock, &ready);
-    struct timeval tv;
-    tv.tv_sec = 0;
-    tv.tv_usec = 0;
-
-    // test for any input on sock (returning immediately, even if
-    // nothing)
-    select(32, &ready, 0, 0, &tv);
-
-    // any new connections?
-    if ( FD_ISSET(sock, &ready) ) {
-       int msgsock = accept(sock, 0, 0);
-       if ( msgsock < 0 ) {
-           SG_LOG( SG_IO, SG_ALERT, 
-                   "Error: accept() failed in write()" );
-           return 0;
-       } else {
-           client_connections.push_back( msgsock );
-       }
-    }
-
-    SG_LOG( SG_IO, SG_INFO, "Client connections = " << 
-           client_connections.size() );
-    for ( int i = 0; i < (int)client_connections.size(); ++i ) {
-       int msgsock = client_connections[i];
-
-       // read and junk any possible incoming messages.
-       // char junk[ SG_IO_MAX_MSG_SIZE ];
-       // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
-
-       // write the interesting data to the socket
-       if ( writesocket(msgsock, buf, length) == SOCKET_ERROR ) {
-           SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
-           error_condition = true;
-       } else {
-#ifdef _POSIX_SYNCHRONIZED_IO
-           // fdatasync(msgsock);
-#else
-           // fsync(msgsock);
-#endif
-       }
-    }
-#endif
-
     if ( error_condition ) {
        return 0;
     }
@@ -475,79 +336,62 @@ int SGSocket::write( const char *buf, const int length ) {
 
 
 // write null terminated string to socket (server)
-int SGSocket::writestring( const char *str ) {
-    if ( sock == INVALID_SOCKET ) {
-       return 0;
-    }
-
+int
+SGSocket::writestring( const char *str )
+{
     int length = strlen( str );
-    return write( str, length );
+    return this->write( str, length );
 }
 
 
 // close the port
-bool SGSocket::close() {
-    if ( sock == INVALID_SOCKET ) {
-       return 0;
-    }
+bool
+SGSocket::close()
+{
+    delete client;
+    client = 0;
 
-    closesocket( sock );
-    if ( sock_style == SOCK_STREAM && msgsock != INVALID_SOCKET ) {
-       sock = msgsock;
-       msgsock = INVALID_SOCKET;
-    }
+    sock.close();
     return true;
 }
 
 
 // configure the socket as non-blocking
-bool SGSocket::nonblock() {
-    if ( sock == INVALID_SOCKET ) {
-       return 0;
+bool
+SGSocket::nonblock()
+{
+    if (sock.getHandle() == -1) {
+       return false;
     }
 
-#if defined(_MSC_VER) || defined(__MINGW32__)
-    u_long arg = 1;
-    if (ioctlsocket( sock, FIONBIO, &arg ) != 0) {
-        int error_code = WSAGetLastError();
-        SG_LOG( SG_IO, SG_ALERT, 
-                "Error " << error_code << ": unable to set non-blocking mode"
-);
-            return false;
-    }
-#else
-    fcntl( sock, F_SETFL, O_NONBLOCK );
-#endif
+    sock.setBlocking( false );
     return true;
 }
 
-#if defined(_MSC_VER) || defined(__MINGW32__)
+int
+SGSocket::poll()
+{
+    netSocket* readers[2];
+
+    readers[0] = client != 0 ? client : &sock;
+    readers[1] = 0;
 
-bool SGSocket::wsock_init = false;
+    netSocket* writers[1];
+    writers[0] = 0;
 
-bool
-SGSocket::wsastartup() {
-    WORD wVersionRequested;
-    WSADATA wsaData;
-
-    //wVersionRequested = MAKEWORD( 2, 2 );
-    wVersionRequested = MAKEWORD( 1, 1 );
-    int err = WSAStartup( wVersionRequested, &wsaData );
-    if (err != 0)
+    int result = netSocket::select( readers, writers, 0 );
+
+    if (result > 0 && is_server && client == 0)
     {
-        SG_LOG( SG_IO, SG_ALERT, "Error: Couldn't load winsock" );
-        return false;
+       // Accept a new client connection
+       netAddress addr;
+       int new_fd = sock.accept( &addr );
+       SG_LOG( SG_IO, SG_INFO, "Accepted connection from "
+               << addr.getHost() << ":" << addr.getPort() );
+       client = new netSocket();
+       client->setHandle( new_fd );
+       return 0;
     }
 
-#if 0
-    if ( LOBYTE( wsaData.wVersion ) != 2 ||
-        HIBYTE( wsaData.wVersion ) != 2 ) {
-        SG_LOG( SG_IO, SG_ALERT, "Couldn't load a suitable winsock");
-        WSACleanup( );
-        return false;
-    }
-#endif
-    wsock_init = true;
-    return true;
+    return result;
 }
-#endif