X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2Fsg_socket.cxx;h=b348ddf19f98cae3c862cce8e328044b4adbb4ec;hb=761b7b93543ad2e8dfd307ee3679b3a2a7082ddc;hp=43735428de4ed348ce07519ac223f30f57e4eaad;hpb=6a3d1895d9d9f90092b393675ba50df7b1cf3c5e;p=simgear.git diff --git a/simgear/io/sg_socket.cxx b/simgear/io/sg_socket.cxx index 43735428..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,16 +24,6 @@ #include -#if !defined(_MSC_VER) && !defined(__MINGW32__) -# 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() -#endif - #if defined( sgi ) #include #endif @@ -41,25 +32,30 @@ #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