X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2Fsg_socket.cxx;h=b348ddf19f98cae3c862cce8e328044b4adbb4ec;hb=761b7b93543ad2e8dfd307ee3679b3a2a7082ddc;hp=d501b29f9410bed468f395ade3b1d3ce9fd90e72;hpb=af383e6c397857e230f0bb4f99bec4ffcf63123b;p=simgear.git diff --git a/simgear/io/sg_socket.cxx b/simgear/io/sg_socket.cxx index d501b29f..b348ddf1 100644 --- a/simgear/io/sg_socket.cxx +++ b/simgear/io/sg_socket.cxx @@ -1,6 +1,7 @@ // sg_socket.cxx -- Socket I/O routines // // Written by Curtis Olson, started November 1999. +// Modified by Bernie Bright , May 2002. // // Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org // @@ -23,49 +24,39 @@ #include -#if ! defined( _MSC_VER ) -# include // select() -# include // socket(), bind(), select(), accept() -# include // socket(), bind(), listen(), accept() -# include // struct sockaddr_in -# include // gethostbyname() -# include // select(), fsync()/fdatasync(), fcntl() -# include // fcntl() -#else -# include // select() -# include // socket(), bind(), listen(), accept(), - // struct sockaddr_in, gethostbyname() -# include -# include -#endif - #if defined( sgi ) #include #endif -#include STL_STRING - #include #include "sg_socket.hxx" -FG_USING_STD(string); - +bool SGSocket::init = false; -SGSocket::SGSocket( const string& host, const string& port, +SGSocket::SGSocket( const string& host, const string& port_, const string& style ) : - save_len(0) + hostname(host), + port_str(port_), + save_len(0), + client(0), + is_tcp(false), + is_server(false), + first_read(false) { - hostname = host; - port_str = port; + if (!init) + { + netInit(); + init = true; + } - if ( style == "udp" ) { - sock_style = SOCK_DGRAM; - } else if ( style == "tcp" ) { - sock_style = SOCK_STREAM; - } else { - sock_style = SOCK_DGRAM; - FG_LOG( FG_IO, FG_ALERT, + if ( style == "tcp" ) + { + is_tcp = true; + } + else if ( style != "udp" ) + { + SG_LOG( SG_IO, SG_ALERT, "Error: SGSocket() unknown style = " << style ); } @@ -73,177 +64,165 @@ SGSocket::SGSocket( const string& host, const string& port, } -SGSocket::~SGSocket() { +SGSocket::~SGSocket() +{ + this->close(); } -int SGSocket::make_server_socket () { - struct sockaddr_in name; - -#if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER ) - int length; -#else - socklen_t length; -#endif - - // Create the socket. - sock = socket (PF_INET, sock_style, 0); - if (sock < 0) { - FG_LOG( FG_IO, FG_ALERT, - "Error: socket() failed in make_server_socket()" ); - return -1; +bool +SGSocket::make_server_socket() +{ + if (!sock.open( is_tcp )) + { + SG_LOG( SG_IO, SG_ALERT, + "Error: socket() failed in make_server_socket()" ); + return false; } - - // 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) { - FG_LOG( FG_IO, FG_ALERT, + + if (sock.bind( "", port ) < 0) + { + SG_LOG( SG_IO, SG_ALERT, "Error: bind() failed in make_server_socket()" ); - return -1; + sock.close(); + return false; } - - // Find the assigned port number - length = sizeof(struct sockaddr_in); - if ( getsockname(sock, (struct sockaddr *) &name, &length) ) { - FG_LOG( FG_IO, FG_ALERT, - "Error: getsockname() failed in make_server_socket()" ); - return -1; - } - port = ntohs(name.sin_port); - return sock; + return true; } -int SGSocket::make_client_socket () { - struct sockaddr_in name; - struct hostent *hp; - - FG_LOG( FG_IO, FG_INFO, "Make client socket()" ); - - // Create the socket. - sock = socket (PF_INET, sock_style, 0); - if (sock < 0) { - FG_LOG( FG_IO, FG_ALERT, - "Error: socket() failed in make_client_socket()" ); - return -1; +bool +SGSocket::make_client_socket() +{ + if (!sock.open( is_tcp )) + { + SG_LOG( SG_IO, SG_ALERT, + "Error: socket() failed in make_client_socket()" ); + return false; } - - // specify address family - name.sin_family = AF_INET; - - // get the hosts official name/info - hp = gethostbyname( hostname.c_str() ); - - // 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) { -#ifdef _MSC_VER - _close(sock); -#else - std::close(sock); -#endif - FG_LOG( FG_IO, FG_ALERT, + SG_LOG( SG_IO, SG_ALERT, "Error: connect() failed in make_client_socket()" ); - return -1; + sock.close(); + return false; } - return sock; + return true; } - // 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( SGProtocolDir dir ) { +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; } else { port = atoi( port_str.c_str() ); } - // client_connections.clear(); - - if ( 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(); - FG_LOG( FG_IO, FG_INFO, "socket is connected to port = " << port ); + if (!make_server_socket()) + { + SG_LOG( SG_IO, SG_ALERT, "SG_IO_IN socket creation failed" ); + return false; + } - if ( sock_style == SOCK_DGRAM ) { + if ( !is_tcp ) + { // Non-blocking UDP - fcntl( sock, F_SETFL, O_NONBLOCK ); - } else { + nonblock(); + } + 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 ( dir == SG_IO_OUT ) { + } + else if (direction == SG_IO_OUT) + { // this means client for now - sock = make_client_socket(); + 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 - fcntl( sock, F_SETFL, O_NONBLOCK ); + nonblock(); } - } else { - FG_LOG( FG_IO, FG_ALERT, - "Error: bidirection mode not available yet for sockets." ); - return false; } + else if (direction == SG_IO_BI && is_tcp) + { + // this means server for TCP sockets - if ( sock < 0 ) { - FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname - << ":" << port ); + // 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. + 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 + sock.listen( SG_MAX_SOCKET_QUEUE ); + } + else + { + SG_LOG( SG_IO, SG_ALERT, + "Error: bidirection mode not available for UDP sockets." ); return false; } + first_read = false; + return true; } // read data from socket (server) // read a block of data of specified size -int SGSocket::read( char *buf, int length ) { - 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; +int +SGSocket::read( char *buf, int length ) +{ + if (sock.getHandle() == -1 && + (client == 0 || client->getHandle() == -1)) + { + return 0; + } // test for any input available on sock (returning immediately, even if // nothing) - select(32, &ready, 0, 0, &tv); + int result = poll(); - if ( FD_ISSET(sock, &ready) ) { -#ifdef _MSC_VER - result = _read( sock, buf, length ); -#else - result = std::read( sock, buf, length ); -#endif - if ( result != length ) { - FG_LOG( FG_IO, FG_INFO, + if (result > 0) + { + result = sock.recv( buf, length ); + + if ( result != length ) + { + SG_LOG( SG_IO, SG_INFO, "Warning: read() not enough bytes." ); } } @@ -253,57 +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 ) { - 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; +int +SGSocket::readline( char *buf, int length ) +{ + if (sock.getHandle() == -1 && + (client == 0 || client->getHandle() == -1)) + { + return 0; + } // test for any input read on sock (returning immediately, even if // nothing) - select(32, &ready, 0, 0, &tv); + int result = this->poll(); - if ( FD_ISSET(sock, &ready) ) { + if (result > 0) + { // read a chunk, keep in the save buffer until we have the // requested amount read - char *buf_ptr = save_buf + save_len; -#ifdef _MSC_VER - result = _read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len ); -#else - result = std::read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len ); -#endif - save_len += result; - // cout << "current read = " << buf_ptr << endl; - // cout << "current save_buf = " << save_buf << endl; - // cout << "save_len = " << save_len << endl; + 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 + { + char *buf_ptr = save_buf + save_len; + result = sock.recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len ); + save_len += result; + } } // 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]; } @@ -314,69 +310,22 @@ int SGSocket::readline( char *buf, int length ) { // write data to socket (client) -int SGSocket::write( char *buf, int length ) { - bool error_condition = false; - -#ifdef _MSC_VER - if ( _write(sock, buf, length) < 0 ) { -#else - if ( std::write(sock, buf, length) < 0 ) { -#endif - FG_LOG( FG_IO, FG_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 ) { - FG_LOG( FG_IO, FG_ALERT, - "Error: accept() failed in write()" ); - return 0; - } else { - client_connections.push_back( msgsock ); - } +int +SGSocket::write( const char *buf, const int length ) +{ + netSocket* s = client == 0 ? &sock : client; + if (s->getHandle() == -1) + { + return 0; } - FG_LOG( FG_IO, FG_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 ); + bool error_condition = false; - // write the interesting data to the socket -#ifdef _MSC_VER - if ( _write(msgsock, buf, length) < 0 ) { -#else - if ( std::write(msgsock, buf, length) < 0 ) { -#endif - FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port ); - error_condition = true; - } else { -#ifdef _POSIX_SYNCHRONIZED_IO - // fdatasync(msgsock); -#else - // fsync(msgsock); -#endif - } + if ( s->send( buf, length ) < 0 ) + { + SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port ); + error_condition = true; } -#endif if ( error_condition ) { return 0; @@ -387,29 +336,62 @@ int SGSocket::write( char *buf, int length ) { // write null terminated string to socket (server) -int SGSocket::writestring( char *str ) { +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 0 - for ( int i = 0; i < (int)client_connections.size(); ++i ) { - int msgsock = client_connections[i]; -#ifdef _MSC_VER - _close( msgsock ); -#else - std::close( msgsock ); -#endif +bool +SGSocket::close() +{ + delete client; + client = 0; + + sock.close(); + return true; +} + + +// configure the socket as non-blocking +bool +SGSocket::nonblock() +{ + if (sock.getHandle() == -1) { + return false; } -#endif -#ifdef _MSC_VER - _close( sock ); -#else - std::close( sock ); -#endif + sock.setBlocking( false ); return true; } + +int +SGSocket::poll() +{ + netSocket* readers[2]; + + readers[0] = client != 0 ? client : &sock; + readers[1] = 0; + + netSocket* writers[1]; + writers[0] = 0; + + int result = netSocket::select( readers, writers, 0 ); + + if (result > 0 && is_server && client == 0) + { + // 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; + } + + return result; +}