Bernie Bright:

Here's a first cut at SGSocket reimplemented using plib.net.  I've
maintained the same TCP server behaviour, that is only one client
connection at a time.  I don't think it is possible within the current
framework to handle simultaneous clients.

I've also added two simple test programs, tcp_client and tcp_server.
This commit is contained in:
curt
2002-05-10 13:53:22 +00:00
parent 6a3d1895d9
commit 761b7b9354
6 changed files with 376 additions and 391 deletions

View File

@@ -32,7 +32,21 @@ else
NETWORK_LIB =
endif
noinst_PROGRAMS = decode_binobj socktest lowtest
noinst_PROGRAMS = decode_binobj socktest lowtest tcp_server tcp_client
tcp_server_SOURCES = tcp_server.cxx
tcp_server_LDADD = \
$(top_builddir)/simgear/io/libsgio.a \
$(top_builddir)/simgear/debug/libsgdebug.a \
-lplibnet
tcp_client_SOURCES = tcp_client.cxx
tcp_client_LDADD = \
$(top_builddir)/simgear/io/libsgio.a \
$(top_builddir)/simgear/debug/libsgdebug.a \
-lplibnet
socktest_SOURCES = socktest.cxx
@@ -42,7 +56,7 @@ socktest_LDADD = \
$(top_builddir)/simgear/debug/libsgdebug.a \
$(top_builddir)/simgear/misc/libsgmisc.a \
$(top_builddir)/simgear/xml/libsgxml.a \
-lz
-lplibnet -lz
lowtest_SOURCES = lowtest.cxx

View File

@@ -22,8 +22,6 @@
#include "iochannel.hxx"
// #include "garmin.hxx"
// #include "nmea.hxx"
// constructor

View File

@@ -1,6 +1,7 @@
// 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
//
@@ -23,16 +24,6 @@
#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
@@ -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) {
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;
}
// Find the assigned port number
length = sizeof(struct sockaddr_in);
if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
SG_LOG( SG_IO, SG_ALERT,
"Error: getsockname() failed in make_server_socket()" );
return INVALID_SOCKET;
}
port = ntohs(name.sin_port);
return sock;
}
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) {
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;
}
// 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 )
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;
}
if (sock.bind( "", port ) < 0)
{
SG_LOG( SG_IO, SG_ALERT,
"Error: bind() failed in make_server_socket()" );
sock.close();
return false;
}
return true;
}
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;
}
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;
return true;
}
// 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 );
}
#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);
int result = poll();
if ( FD_ISSET(sock, &ready) ) {
// cout << "data ready" << endl;
if (result > 0)
{
result = sock.recv( buf, length );
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 ) {
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();
}
}
} else {
if (is_tcp)
{
char *buf_ptr = save_buf + save_len;
result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - 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;
}
// 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 ) {
bool
SGSocket::nonblock()
{
if (sock.getHandle() == -1) {
return false;
}
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;
}
#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
return true;
return result;
}
#if defined(_MSC_VER) || defined(__MINGW32__)
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;
}
#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

View File

@@ -39,11 +39,9 @@
#include <simgear/math/sg_types.hxx>
#include <simgear/io/iochannel.hxx>
SG_USING_STD(string);
#include <plib/netSocket.h>
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <winsock.h>
#endif
SG_USING_STD(string);
#define SG_MAX_SOCKET_QUEUE 32
@@ -53,13 +51,6 @@ SG_USING_STD(string);
*/
class SGSocket : public SGIOChannel {
public:
#if defined(_MSC_VER) || defined(__MINGW32__)
typedef SOCKET SocketType;
#else
typedef int SocketType;
# define INVALID_SOCKET (-1)
#endif
private:
string hostname;
string port_str;
@@ -67,31 +58,23 @@ private:
char save_buf[ 2 * SG_IO_MAX_MSG_SIZE ];
int save_len;
SocketType sock;
SocketType msgsock;
short unsigned int port;
int sock_style; // SOCK_STREAM or SOCK_DGRAM
netSocket sock;
netSocket* client;
unsigned short port;
bool is_tcp;
bool is_server;
bool first_read;
static bool init;
// make a server (master listening) socket
SocketType make_server_socket();
bool make_server_socket();
// make a client socket
SocketType make_client_socket();
bool make_client_socket();
// wrapper functions
int readsocket( int fd, void *buf, size_t count );
int writesocket( int fd, const void *buf, size_t count );
#if !defined(_MSC_VER) && !defined(__MINGW32__)
int closesocket(int fd);
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
// Ensure winsock has been initialised.
static bool wsock_init;
static bool wsastartup();
#endif
// Poll for new connections or data to read.
int poll();
public:

77
simgear/io/tcp_client.cxx Normal file
View File

@@ -0,0 +1,77 @@
#include <iostream>
#include <unistd.h>
#include <simgear/debug/logstream.hxx>
#include "sg_socket.hxx"
using std::cout;
class TcpClient
{
public:
TcpClient( const char* host, const char* port );
~TcpClient();
bool open();
bool process();
bool close();
private:
SGIOChannel* channel;
};
TcpClient::TcpClient( const char* host, const char* port )
{
channel = new SGSocket( host, port, "tcp" );
}
TcpClient::~TcpClient()
{
delete channel;
}
bool
TcpClient::open()
{
return channel->open( SG_IO_OUT );
}
bool
TcpClient::process()
{
char wbuf[1024];
sprintf( wbuf, "hello world\n" );
int length = channel->writestring( wbuf );
cout << "writestring returned " << length << "\n";
return true;
}
bool
TcpClient::close()
{
return channel->close();
}
int
main()
{
sglog().setLogLevels( SG_ALL, SG_INFO );
TcpClient client( "localhost", "5500" );
if (!client.open())
{
cout << "client open failed\n";
return 0;
}
for (int i = 0; i < 3; ++i)
{
client.process();
sleep(1);
}
//client.close();
return 0;
}

69
simgear/io/tcp_server.cxx Normal file
View File

@@ -0,0 +1,69 @@
#include <simgear/debug/logstream.hxx>
#include <string>
#include <iostream>
#include "sg_socket.hxx"
using std::string;
using std::cout;
class TcpServer
{
public:
TcpServer();
bool open();
bool process();
bool close();
private:
SGIOChannel* channel;
};
TcpServer::TcpServer()
{
channel = new SGSocket( "", "5500", "tcp" );
}
bool
TcpServer::open()
{
channel->open( SG_IO_BI );
return true;
}
bool
TcpServer::process()
{
char buf[1024];
int len;
while ((len = channel->readline( buf, sizeof(buf) )) > 0)
{
cout << len << ": " << buf;
}
return true;
}
bool
TcpServer::close()
{
return channel->close();
}
int
main()
{
sglog().setLogLevels( SG_ALL, SG_INFO );
TcpServer server;
server.open();
SG_LOG( SG_IO, SG_INFO, "Created TCP server" );
while (1)
{
server.process();
}
server.close();
return 0;
}