From 6b3a05e23fc8f8121f24824711917e7255f17fa8 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 22 Jul 2010 13:34:05 +0100 Subject: [PATCH] Move PLIB netSocket into SimGear, as simgear::Socket, and update the wrapper classes. --- projects/VC90/SimGear.vcproj | 8 + simgear/io/Makefile.am | 30 +-- simgear/io/raw_socket.cxx | 488 +++++++++++++++++++++++++++++++++++ simgear/io/raw_socket.hxx | 110 ++++++++ simgear/io/sg_socket.cxx | 20 +- simgear/io/sg_socket.hxx | 24 +- simgear/io/sg_socket_udp.cxx | 4 - simgear/io/sg_socket_udp.hxx | 19 +- 8 files changed, 635 insertions(+), 68 deletions(-) create mode 100644 simgear/io/raw_socket.cxx create mode 100644 simgear/io/raw_socket.hxx diff --git a/projects/VC90/SimGear.vcproj b/projects/VC90/SimGear.vcproj index 24484e8c..7ef14fc9 100644 --- a/projects/VC90/SimGear.vcproj +++ b/projects/VC90/SimGear.vcproj @@ -495,6 +495,14 @@ RelativePath="..\..\simgear\io\sg_socket_udp.hxx" > + + + + +#endif + +#include + +#include "sg_socket.hxx" + +#if defined(_WIN32) && !defined(__CYGWIN__) +# define WINSOCK // use winsock convetions, otherwise standard POSIX +#endif + +#include +#include +#include +#include +#include // for snprintf + +#if defined(WINSOCK) +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#if defined(_MSC_VER) && !defined(socklen_t) +#define socklen_t int +#endif + +#include +#include + +namespace simgear +{ + +IPAddress::IPAddress ( const char* host, int port ) +{ + set ( host, port ) ; +} + + +void IPAddress::set ( const char* host, int port ) +{ + memset(this, 0, sizeof(IPAddress)); + + sin_family = AF_INET ; + sin_port = htons (port); + + /* Convert a string specifying a host name or one of a few symbolic + ** names to a numeric IP address. This usually calls gethostbyname() + ** to do the work; the names "" and "" are special. + */ + + if (!host || host[0] == '\0') { + sin_addr = INADDR_ANY; + return; + } + + if (strcmp(host, "") == 0) { + sin_addr = INADDR_BROADCAST; + return; + } + + sin_addr = inet_addr ( host ) ; + if (sin_addr != INADDR_NONE) { + return; + } +// finally, try gethostbyname + struct hostent *hp = gethostbyname ( host ) ; + if (!hp) { + SG_LOG(SG_IO, SG_WARN, "gethostbyname failed for " << host); + sin_addr = INADDR_ANY ; + return; + } + + memcpy ( (char *) &sin_addr, hp->h_addr, hp->h_length ) ; +} + + +/* Create a string object representing an IP address. + This is always a string of the form 'dd.dd.dd.dd' (with variable + size numbers). */ + +const char* IPAddress::getHost () const +{ +#if 0 + const char* buf = inet_ntoa ( sin_addr ) ; +#else + static char buf [32]; + long x = ntohl(sin_addr); + sprintf(buf, "%d.%d.%d.%d", + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff ); +#endif + return buf; +} + +unsigned int IPAddress::getIP () const +{ + return sin_addr; +} + +unsigned int IPAddress::getPort() const +{ + return ntohs(sin_port); +} + +unsigned int IPAddress::getFamily () const +{ + return sin_family; +} + +const char* IPAddress::getLocalHost () +{ + //gethostbyname(gethostname()) + + char buf[256]; + memset(buf, 0, sizeof(buf)); + gethostname(buf, sizeof(buf)-1); + const hostent *hp = gethostbyname(buf); + + if (hp && *hp->h_addr_list) + { + in_addr addr = *((in_addr*)*hp->h_addr_list); + const char* host = inet_ntoa(addr); + + if ( host ) + return host ; + } + + return "127.0.0.1" ; +} + + +bool IPAddress::getBroadcast () const +{ + return sin_addr == INADDR_BROADCAST; +} + + +Socket::Socket () +{ + handle = -1 ; +} + + +Socket::~Socket () +{ + close () ; +} + + +void Socket::setHandle (int _handle) +{ + close () ; + handle = _handle ; +} + + +bool Socket::open ( bool stream ) +{ + close () ; + handle = ::socket ( AF_INET, (stream? SOCK_STREAM: SOCK_DGRAM), 0 ) ; + return (handle != -1); +} + + +void Socket::setBlocking ( bool blocking ) +{ + assert ( handle != -1 ) ; + +#if defined(WINSOCK) + u_long nblocking = blocking? 0: 1; + ::ioctlsocket(handle, FIONBIO, &nblocking); +#else + + int delay_flag = ::fcntl (handle, F_GETFL, 0); + + if (blocking) + delay_flag &= (~O_NDELAY); + else + delay_flag |= O_NDELAY; + + ::fcntl (handle, F_SETFL, delay_flag); +#endif +} + + +void Socket::setBroadcast ( bool broadcast ) +{ + assert ( handle != -1 ) ; + int result; + if ( broadcast ) { + int one = 1; +#if defined(_WIN32) || defined(__CYGWIN__) + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one) ); +#else + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one) ); +#endif + } else { + result = ::setsockopt( handle, SOL_SOCKET, SO_BROADCAST, NULL, 0 ); + } + + if ( result < 0 ) { + throw sg_exception("Socket::setBroadcast failed"); + } +} + + +int Socket::bind ( const char* host, int port ) +{ + assert ( handle != -1 ) ; + IPAddress addr ( host, port ) ; + return ::bind(handle,(const sockaddr*)&addr,sizeof(IPAddress)); +} + + +int Socket::listen ( int backlog ) +{ + assert ( handle != -1 ) ; + return ::listen(handle,backlog); +} + + +int Socket::accept ( IPAddress* addr ) +{ + assert ( handle != -1 ) ; + + if ( addr == NULL ) + { + return ::accept(handle,NULL,NULL); + } + else + { + socklen_t addr_len = (socklen_t) sizeof(IPAddress) ; + return ::accept(handle,(sockaddr*)addr,&addr_len); + } +} + + +int Socket::connect ( const char* host, int port ) +{ + assert ( handle != -1 ) ; + IPAddress addr ( host, port ) ; + if ( addr.getBroadcast() ) { + setBroadcast( true ); + } + return ::connect(handle,(const sockaddr*)&addr,sizeof(IPAddress)); +} + + +int Socket::send (const void * buffer, int size, int flags) +{ + assert ( handle != -1 ) ; + return ::send (handle, (const char*)buffer, size, flags); +} + + +int Socket::sendto ( const void * buffer, int size, + int flags, const IPAddress* to ) +{ + assert ( handle != -1 ) ; + return ::sendto(handle,(const char*)buffer,size,flags, + (const sockaddr*)to,sizeof(IPAddress)); +} + + +int Socket::recv (void * buffer, int size, int flags) +{ + assert ( handle != -1 ) ; + return ::recv (handle, (char*)buffer, size, flags); +} + + +int Socket::recvfrom ( void * buffer, int size, + int flags, IPAddress* from ) +{ + assert ( handle != -1 ) ; + socklen_t fromlen = (socklen_t) sizeof(IPAddress) ; + return ::recvfrom(handle,(char*)buffer,size,flags,(sockaddr*)from,&fromlen); +} + + +void Socket::close (void) +{ + if ( handle != -1 ) + { +#if defined(WINSOCK) + ::closesocket( handle ); +#else + ::close( handle ); +#endif + handle = -1 ; + } +} + + +bool Socket::isNonBlockingError () +{ +#if defined(WINSOCK) + int wsa_errno = WSAGetLastError(); + if ( wsa_errno != 0 ) + { + WSASetLastError(0); + ulSetError(UL_WARNING,"WSAGetLastError() => %d",wsa_errno); + switch (wsa_errno) { + case WSAEWOULDBLOCK: // always == NET_EAGAIN? + case WSAEALREADY: + case WSAEINPROGRESS: + return true; + } + } + return false; +#else + switch (errno) { + case EWOULDBLOCK: // always == NET_EAGAIN? + case EALREADY: + case EINPROGRESS: + return true; + } + return false; + +#endif +} + + +////////////////////////////////////////////////////////////////////// +// +// modified version by os +// +////////////////////////////////////////////////////////////////////// +int Socket::select ( Socket** reads, Socket** writes, int timeout ) +{ + fd_set r,w; + int retval; + + FD_ZERO (&r); + FD_ZERO (&w); + + int i, k ; + int num = 0 ; + + if ( reads ) + { + for ( i=0; reads[i]; i++ ) + { + int fd = reads[i]->getHandle(); + FD_SET (fd, &r); + num++; + } + } + + if ( writes ) + { + for ( i=0; writes[i]; i++ ) + { + int fd = writes[i]->getHandle(); + FD_SET (fd, &w); + num++; + } + } + + if (!num) + return num ; + + /* Set up the timeout */ + struct timeval tv ; + tv.tv_sec = timeout/1000; + tv.tv_usec = (timeout%1000)*1000; + + // It bothers me that select()'s first argument does not appear to + // work as advertised... [it hangs like this if called with + // anything less than FD_SETSIZE, which seems wasteful?] + + // Note: we ignore the 'exception' fd_set - I have never had a + // need to use it. The name is somewhat misleading - the only + // thing I have ever seen it used for is to detect urgent data - + // which is an unportable feature anyway. + + retval = ::select (FD_SETSIZE, &r, &w, 0, &tv); + + //remove sockets that had no activity + + num = 0 ; + + if ( reads ) + { + for ( k=i=0; reads[i]; i++ ) + { + int fd = reads[i]->getHandle(); + if ( FD_ISSET (fd, &r) ) + { + reads[k++] = reads[i]; + num++; + } + } + reads[k] = NULL ; + } + + if ( writes ) + { + for ( k=i=0; writes[i]; i++ ) + { + int fd = writes[i]->getHandle(); + if ( FD_ISSET (fd, &w) ) + { + writes[k++] = writes[i]; + num++; + } + } + writes[k] = NULL ; + } + + if (retval == 0) // timeout + return (-2); + if (retval == -1)// error + return (-1); + + return num ; +} + + +/* Init/Exit functions */ + +static void netExit ( void ) +{ +#if defined(WINSOCK) + /* Clean up windows networking */ + if ( WSACleanup() == SOCKET_ERROR ) { + if ( WSAGetLastError() == WSAEINPROGRESS ) { + WSACancelBlockingCall(); + WSACleanup(); + } + } +#endif +} + +int Socket::initSockets() +{ + assert ( sizeof(sockaddr_in) == sizeof(IPAddress) ) ; + +#if defined(WINSOCK) + /* Start up the windows networking */ + WORD version_wanted = MAKEWORD(1,1); + WSADATA wsaData; + + if ( WSAStartup(version_wanted, &wsaData) != 0 ) { + ulSetError(UL_WARNING,"Couldn't initialize Winsock 1.1"); + return(-1); + } +#endif + + atexit( netExit ) ; + return(0); +} + + +} // of namespace simgear + diff --git a/simgear/io/raw_socket.hxx b/simgear/io/raw_socket.hxx new file mode 100644 index 00000000..f68ff105 --- /dev/null +++ b/simgear/io/raw_socket.hxx @@ -0,0 +1,110 @@ +/* + simgear::Socket, adapted from PLIB netSocket by James Turner + Copyright (C) 2010 James Turner + + PLIB - A Suite of Portable Game Libraries + Copyright (C) 1998,2002 Steve Baker + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SG_IO_SOCKET_HXX +#define SG_IO_SOCKET_HXX + +#include + +#if defined(__APPLE__) +# include +#endif + +namespace simgear +{ + +/* + * Socket address, internet style. + */ +class IPAddress +{ + /* DANGER!!! This MUST match 'struct sockaddr_in' exactly! */ +#ifdef __APPLE__ + __uint8_t sin_len; + __uint8_t sin_family; + in_port_t sin_port; + in_addr_t sin_addr; + char sin_zero[8]; +#else + short sin_family ; + unsigned short sin_port ; + unsigned int sin_addr ; + char sin_zero [ 8 ] ; +#endif + +public: + IPAddress () {} + IPAddress ( const char* host, int port ) ; + + void set ( const char* host, int port ) ; + const char* getHost () const ; + unsigned int getPort() const ; + unsigned int getIP () const ; + unsigned int getFamily () const ; + static const char* getLocalHost () ; + + bool getBroadcast () const ; +}; + + +/* + * Socket type + */ +class Socket +{ + int handle ; + +public: + + Socket () ; + virtual ~Socket () ; + + static int initSockets(); + + int getHandle () const { return handle; } + void setHandle (int handle) ; + + bool open ( bool stream=true ) ; + void close ( void ) ; + int bind ( const char* host, int port ) ; + int listen ( int backlog ) ; + int accept ( IPAddress* addr ) ; + int connect ( const char* host, int port ) ; + int send ( const void * buffer, int size, int flags = 0 ) ; + int sendto ( const void * buffer, int size, int flags, const IPAddress* to ) ; + int recv ( void * buffer, int size, int flags = 0 ) ; + int recvfrom ( void * buffer, int size, int flags, IPAddress* from ) ; + + void setBlocking ( bool blocking ) ; + void setBroadcast ( bool broadcast ) ; + + static bool isNonBlockingError () ; + + static int select ( Socket** reads, Socket** writes, int timeout ) ; +} ; + +//const char* netFormat ( const char* fmt, ... ) ; + +} // of namespace simgear + +#endif // SG_IO_SOCKET_HXX + diff --git a/simgear/io/sg_socket.cxx b/simgear/io/sg_socket.cxx index 8dcbd6be..14ae2969 100644 --- a/simgear/io/sg_socket.cxx +++ b/simgear/io/sg_socket.cxx @@ -27,10 +27,6 @@ #include -#if defined( sgi ) -#include -#endif - #include #include @@ -52,8 +48,8 @@ SGSocket::SGSocket( const string& host, const string& port_, { if (!init) { - netInit(NULL, NULL); // plib-1.4.2 compatible - init = true; + simgear::Socket::initSockets(); + init = true; } if ( style == "tcp" ) @@ -326,7 +322,7 @@ SGSocket::readline( char *buf, int length ) int SGSocket::write( const char *buf, const int length ) { - netSocket* s = client == 0 ? &sock : client; + simgear::Socket* s = client == 0 ? &sock : client; if (s->getHandle() == -1) { return 0; @@ -384,24 +380,24 @@ SGSocket::nonblock() int SGSocket::poll() { - netSocket* readers[2]; + simgear::Socket* readers[2]; readers[0] = client != 0 ? client : &sock; readers[1] = 0; - netSocket* writers[1]; + simgear::Socket* writers[1]; writers[0] = 0; - int result = netSocket::select( readers, writers, timeout ); + int result = simgear::Socket::select( readers, writers, timeout ); if (result > 0 && is_server && client == 0) { // Accept a new client connection - netAddress addr; + simgear::IPAddress addr; int new_fd = sock.accept( &addr ); SG_LOG( SG_IO, SG_INFO, "Accepted connection from " << addr.getHost() << ":" << addr.getPort() ); - client = new netSocket(); + client = new simgear::Socket(); client->setHandle( new_fd ); return 0; } diff --git a/simgear/io/sg_socket.hxx b/simgear/io/sg_socket.hxx index 78c9d2d8..8341cb34 100644 --- a/simgear/io/sg_socket.hxx +++ b/simgear/io/sg_socket.hxx @@ -27,21 +27,13 @@ #ifndef _SG_SOCKET_HXX #define _SG_SOCKET_HXX - -#ifndef __cplusplus -# error This library requires C++ -#endif - #include #include #include #include - -#include - -using std::string; +#include #define SG_MAX_SOCKET_QUEUE 32 @@ -52,14 +44,14 @@ using std::string; class SGSocket : public SGIOChannel { public: private: - string hostname; - string port_str; + std::string hostname; + std::string port_str; char save_buf[ 2 * SG_IO_MAX_MSG_SIZE ]; int save_len; - netSocket sock; - netSocket* client; + simgear::Socket sock; + simgear::Socket* client; unsigned short port; bool is_tcp; bool is_server; @@ -126,7 +118,7 @@ public: * @param port port number if we care to choose one. * @param style specify "udp" or "tcp" */ - SGSocket( const string& host, const string& port, const string& style ); + SGSocket( const std::string& host, const std::string& port, const std::string& style ); /** Destructor */ ~SGSocket(); @@ -161,10 +153,10 @@ public: inline void set_timeout(int i) { timeout = i; } /** @return the remote host name */ - inline string get_hostname() const { return hostname; } + inline std::string get_hostname() const { return hostname; } /** @return the port number (in string form) */ - inline string get_port_str() const { return port_str; } + inline std::string get_port_str() const { return port_str; } }; diff --git a/simgear/io/sg_socket_udp.cxx b/simgear/io/sg_socket_udp.cxx index 97f0d8e0..bdc04d11 100644 --- a/simgear/io/sg_socket_udp.cxx +++ b/simgear/io/sg_socket_udp.cxx @@ -26,10 +26,6 @@ #include -#if defined( sgi ) -#include -#endif - #include #include "sg_socket_udp.hxx" diff --git a/simgear/io/sg_socket_udp.hxx b/simgear/io/sg_socket_udp.hxx index 9d8e8eb6..69a5b027 100644 --- a/simgear/io/sg_socket_udp.hxx +++ b/simgear/io/sg_socket_udp.hxx @@ -28,20 +28,13 @@ #define _SG_SOCKET_UDP_HXX -#ifndef __cplusplus -# error This library requires C++ -#endif - -#include - #include #include #include #include - -using std::string; +#include /** * A UDP socket I/O class based on SGIOChannel and plib/net. @@ -50,10 +43,10 @@ class SGSocketUDP : public SGIOChannel { private: - netSocket sock; + simgear::Socket sock; - string hostname; - string port_str; + std::string hostname; + std::string port_str; char save_buf[ 2 * SG_IO_MAX_MSG_SIZE ]; int save_len; @@ -128,10 +121,10 @@ public: bool setBlocking( bool value ); /** @return the remote host name */ - inline string get_hostname() const { return hostname; } + inline std::string get_hostname() const { return hostname; } /** @return the port number (in string form) */ - inline string get_port_str() const { return port_str; } + inline std::string get_port_str() const { return port_str; } }; -- 2.39.5