// 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 );
}
}
-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;
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;
// 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." );
}
// 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];
}
// 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;
}
// 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