X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fio%2Fsg_socket.cxx;h=905639b91d2c01f437dbe9a01d2b9827246f5658;hb=a66d1ad8552e910adb72d442177e2c9f21660857;hp=d501b29f9410bed468f395ade3b1d3ce9fd90e72;hpb=af383e6c397857e230f0bb4f99bec4ffcf63123b;p=simgear.git diff --git a/simgear/io/sg_socket.cxx b/simgear/io/sg_socket.cxx index d501b29f..905639b9 100644 --- a/simgear/io/sg_socket.cxx +++ b/simgear/io/sg_socket.cxx @@ -23,7 +23,7 @@ #include -#if ! defined( _MSC_VER ) +#if !defined(_MSC_VER) # include // select() # include // socket(), bind(), select(), accept() # include // socket(), bind(), listen(), accept() @@ -31,33 +31,28 @@ # 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); - SGSocket::SGSocket( const string& host, const string& port, const string& style ) : + hostname(host), + port_str(port), save_len(0) { - hostname = host; - port_str = port; +#if defined(_MSC_VER) + if (!wsock_init && !wsastartup()) { + SG_LOG( SG_IO, SG_ALERT, "Winsock not available"); + } +#endif if ( style == "udp" ) { sock_style = SOCK_DGRAM; @@ -65,7 +60,7 @@ SGSocket::SGSocket( const string& host, const string& port, sock_style = SOCK_STREAM; } else { sock_style = SOCK_DGRAM; - FG_LOG( FG_IO, FG_ALERT, + SG_LOG( SG_IO, SG_ALERT, "Error: SGSocket() unknown style = " << style ); } @@ -77,7 +72,7 @@ SGSocket::~SGSocket() { } -int SGSocket::make_server_socket () { +SGSocket::SocketType SGSocket::make_server_socket () { struct sockaddr_in name; #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER ) @@ -88,10 +83,10 @@ int SGSocket::make_server_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_server_socket()" ); - return -1; + if (sock == INVALID_SOCKET) { + SG_LOG( SG_IO, SG_ALERT, + "Error: socket() failed in make_server_socket()" ); + return INVALID_SOCKET; } // Give the socket a name. @@ -99,18 +94,18 @@ int SGSocket::make_server_socket () { 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, - "Error: bind() failed in make_server_socket()" ); - return -1; + 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; } - + // Find the assigned port number length = sizeof(struct sockaddr_in); if ( getsockname(sock, (struct sockaddr *) &name, &length) ) { - FG_LOG( FG_IO, FG_ALERT, + SG_LOG( SG_IO, SG_ALERT, "Error: getsockname() failed in make_server_socket()" ); - return -1; + return INVALID_SOCKET; } port = ntohs(name.sin_port); @@ -118,18 +113,18 @@ int SGSocket::make_server_socket () { } -int SGSocket::make_client_socket () { +SGSocket::SocketType SGSocket::make_client_socket () { struct sockaddr_in name; struct hostent *hp; - FG_LOG( FG_IO, FG_INFO, "Make client socket()" ); + SG_LOG( SG_IO, SG_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; + if (sock == INVALID_SOCKET) { + SG_LOG( SG_IO, SG_ALERT, + "Error: socket() failed in make_server_socket()" ); + return INVALID_SOCKET; } // specify address family @@ -137,6 +132,10 @@ int SGSocket::make_client_socket () { // 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; + } // Connect this socket to the host and the port specified on the // command line @@ -148,26 +147,48 @@ int SGSocket::make_client_socket () { name.sin_port = htons(port); if ( connect(sock, (struct sockaddr *) &name, - sizeof(struct sockaddr_in)) < 0 ) + sizeof(struct sockaddr_in)) != 0 ) { -#ifdef _MSC_VER - _close(sock); -#else - std::close(sock); -#endif - FG_LOG( FG_IO, FG_ALERT, + closesocket(sock); + SG_LOG( SG_IO, SG_ALERT, "Error: connect() failed in make_client_socket()" ); - return -1; + return INVALID_SOCKET; } return sock; } +// Wrapper functions +size_t SGSocket::readsocket( int fd, void *buf, size_t count ) { +#if defined(_MSC_VER) + return ::recv( fd, (char *)buf, count, 0 ); +#else + return ::read( fd, buf, count ); +#endif +} + +size_t SGSocket::writesocket( int fd, const void *buf, size_t count ) { +#if defined(_MSC_VER) + return ::send( fd, (const char*)buf, count, 0 ); +#else + return ::write( fd, buf, count ); +#endif +} + +#if !defined(_MSC_VER) +int SGSocket::closesocket( int fd ) { + return ::close( fd ); +} +#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( SGProtocolDir dir ) { +bool SGSocket::open( const SGProtocolDir d ) { + set_dir( d ); + if ( port_str == "" || port_str == "any" ) { port = 0; } else { @@ -176,45 +197,69 @@ bool SGSocket::open( SGProtocolDir dir ) { // client_connections.clear(); - if ( dir == SG_IO_IN ) { + if ( get_dir() == 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 ( sock == INVALID_SOCKET ) { + SG_LOG( SG_IO, SG_ALERT, "socket creation failed" ); + return false; + } + + SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port ); if ( sock_style == SOCK_DGRAM ) { // Non-blocking UDP - fcntl( sock, F_SETFL, O_NONBLOCK ); + nonblock(); } else { // Blocking TCP // Specify the maximum length of the connection queue listen( sock, SG_MAX_SOCKET_QUEUE ); } - } else if ( dir == SG_IO_OUT ) { + } else if ( get_dir() == SG_IO_OUT ) { // this means client for now sock = make_client_socket(); + // TODO: check for error. if ( sock_style == SOCK_DGRAM ) { // Non-blocking UDP - fcntl( sock, F_SETFL, O_NONBLOCK ); + nonblock(); } + } else if ( get_dir() == SG_IO_BI && sock_style == SOCK_STREAM ) { + // 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 ); + + // Blocking TCP + // Specify the maximum length of the connection queue + listen( sock, SG_MAX_SOCKET_QUEUE ); } else { - FG_LOG( FG_IO, FG_ALERT, - "Error: bidirection mode not available yet for sockets." ); + SG_LOG( SG_IO, SG_ALERT, + "Error: bidirection mode not available for UDP sockets." ); return false; } if ( sock < 0 ) { - FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname + 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; } @@ -222,8 +267,11 @@ bool SGSocket::open( SGProtocolDir dir ) { // read data from socket (server) // read a block of data of specified size int SGSocket::read( char *buf, int length ) { - int result = 0; + if ( sock == INVALID_SOCKET ) { + return 0; + } + int result = 0; // check for potential input fd_set ready; FD_ZERO(&ready); @@ -237,13 +285,22 @@ int SGSocket::read( char *buf, int length ) { select(32, &ready, 0, 0, &tv); if ( FD_ISSET(sock, &ready) ) { -#ifdef _MSC_VER - result = _read( sock, buf, length ); -#else - result = std::read( sock, buf, length ); -#endif + // 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 ); + } + if ( result != length ) { - FG_LOG( FG_IO, FG_INFO, + SG_LOG( SG_IO, SG_INFO, "Warning: read() not enough bytes." ); } } @@ -254,7 +311,11 @@ 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; + if ( sock == INVALID_SOCKET ) { + return 0; + } + + // cout << "sock = " << sock << endl; // check for potential input fd_set ready; @@ -266,22 +327,55 @@ int SGSocket::readline( char *buf, int length ) { // test for any input read on sock (returning immediately, even if // nothing) - select(32, &ready, 0, 0, &tv); + int result = select(32, &ready, 0, 0, &tv); + // cout << "result = " << result << endl; if ( FD_ISSET(sock, &ready) ) { + // cout << "fd change state\n"; // 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; + if ( sock_style == SOCK_STREAM ) { + // cout << "sock_stream\n"; + if ( msgsock == INVALID_SOCKET ) { + // cout << "msgsock == invalid\n"; + msgsock = accept(sock, 0, 0); + closesocket(sock); + sock = msgsock; + } 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." ); + closesocket(sock); + open( get_dir() ); + } + } + } else { + char *buf_ptr = save_buf + save_len; + result = readsocket( sock, 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 @@ -314,15 +408,15 @@ int SGSocket::readline( char *buf, int length ) { // write data to socket (client) -int SGSocket::write( char *buf, int length ) { +int SGSocket::write( const char *buf, const int length ) { + if ( sock == INVALID_SOCKET ) { + return 0; + } + 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 ); + if ( writesocket(sock, buf, length) < 0 ) { + SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port ); error_condition = true; } @@ -343,7 +437,7 @@ int SGSocket::write( char *buf, int length ) { if ( FD_ISSET(sock, &ready) ) { int msgsock = accept(sock, 0, 0); if ( msgsock < 0 ) { - FG_LOG( FG_IO, FG_ALERT, + SG_LOG( SG_IO, SG_ALERT, "Error: accept() failed in write()" ); return 0; } else { @@ -351,7 +445,7 @@ int SGSocket::write( char *buf, int length ) { } } - FG_LOG( FG_IO, FG_INFO, "Client connections = " << + 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]; @@ -361,12 +455,8 @@ int SGSocket::write( char *buf, int length ) { // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE ); // 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 ); + 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 @@ -387,7 +477,11 @@ 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 ) { + if ( sock == INVALID_SOCKET ) { + return 0; + } + int length = strlen( str ); return write( str, length ); } @@ -395,21 +489,63 @@ int SGSocket::writestring( char *str ) { // 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 ); + if ( sock == INVALID_SOCKET ) { + return 0; + } + + closesocket( sock ); + return true; +} + + +// configure the socket as non-blocking +bool SGSocket::nonblock() { + if ( sock == INVALID_SOCKET ) { + return 0; + } + +#if defined(_MSC_VER) + 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 - std::close( msgsock ); + fcntl( sock, F_SETFL, O_NONBLOCK ); #endif + return true; +} + +#if defined(_MSC_VER) + +bool SGSocket::wsock_init = false; + +bool +SGSocket::wsastartup() { + WORD wVersionRequested; + WSADATA wsaData; + + //wVersionRequested = MAKEWORD( 2, 2 ); + wVersionRequested = MAKEWORD( 1, 1 ); + int err = WSAStartup( wVersionRequested, &wsaData ); + if (err != 0) + { + SG_LOG( SG_IO, SG_ALERT, "Error: Couldn't load winsock" ); + return false; } -#endif -#ifdef _MSC_VER - _close( sock ); -#else - std::close( sock ); +#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; } +#endif