From 57163baca0d8df24ad51049b62a650c6986f4cec Mon Sep 17 00:00:00 2001 From: curt Date: Sat, 20 Nov 1999 15:41:48 +0000 Subject: [PATCH] Added first stab at a socket class. Fixed readline() bug in fg_serial.cxx and fg_socket.cxx. Fixed process() bug in nmea.cxx and garmin.cxx. --- src/Network/fg_serial.cxx | 2 +- src/Network/fg_socket.cxx | 304 ++++++++++++++++++++++++++++++++++++++ src/Network/fg_socket.hxx | 95 ++++++++++++ src/Network/garmin.cxx | 42 ++++-- src/Network/nmea.cxx | 4 +- 5 files changed, 428 insertions(+), 19 deletions(-) create mode 100644 src/Network/fg_socket.cxx create mode 100644 src/Network/fg_socket.hxx diff --git a/src/Network/fg_serial.cxx b/src/Network/fg_serial.cxx index 162c62fc6..5f70bea4b 100644 --- a/src/Network/fg_serial.cxx +++ b/src/Network/fg_serial.cxx @@ -116,7 +116,7 @@ int FGSerial::readline( char *buf, int length ) { // shift save buffer for ( i = result; i < save_len; ++i ) { - save_buf[ i - result ] = save_buf[result]; + save_buf[ i - result ] = save_buf[i]; } save_len -= result; diff --git a/src/Network/fg_socket.cxx b/src/Network/fg_socket.cxx new file mode 100644 index 000000000..64882c3f5 --- /dev/null +++ b/src/Network/fg_socket.cxx @@ -0,0 +1,304 @@ +// fg_socket.cxx -- Socket I/O routines +// +// Written by Curtis Olson, started November 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#include + +#include // select() +#include // socket(), bind(), select(), accept() +#include // socket(), bind(), listen(), accept() +#include // struct sockaddr_in +#include // gethostbyname() +#include // select() + +#include STL_STRING + +#include + +#include "fg_socket.hxx" + +FG_USING_STD(string); + + +FGSocket::FGSocket() : + save_len(0) +{ +} + + +FGSocket::~FGSocket() { +} + + +int FGSocket::make_server_socket () { + struct sockaddr_in name; + socklen_t length; + + // Create the socket. + sock = socket (PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + FG_LOG( FG_IO, FG_ALERT, + "Error: socket() failed in make_server_socket()" ); + return -1; + } + + // 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) { + FG_LOG( FG_IO, FG_ALERT, + "Error: bind() failed in make_server_socket()" ); + return -1; + } + + // Find the assigned port number + length = sizeof(struct sockaddr_in); + if ( getsockname(sock, (struct sockaddr *) &name, &length) ) { + FG_LOG( FG_IO, FG_ALERT, + "Error: getsockname() failed in make_server_socket()" ); + return -1; + } + port = ntohs(name.sin_port); + + return sock; +} + + +int FGSocket::make_client_socket () { + struct sockaddr_in name; + struct hostent *hp; + + // Create the socket. + sock = socket (PF_INET, SOCK_STREAM, 0); + if (sock < 0) { + FG_LOG( FG_IO, FG_ALERT, + "Error: socket() failed in make_client_socket()" ); + return -1; + } + + // specify address family + name.sin_family = AF_INET; + + // get the hosts official name/info + hp = gethostbyname( hostname.c_str() ); + + // Connect this socket to the host and the port specified on the + // command line + bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length); + name.sin_port = htons(port); + + if ( connect(sock, (struct sockaddr *) &name, + sizeof(struct sockaddr_in)) < 0 ) + { + std::close(sock); + FG_LOG( FG_IO, FG_ALERT, + "Error: connect() failed in make_client_socket()" ); + return -1; + } + + return sock; +} + + +// If specified as a server (out direction for now) open the master +// listening socket. If specified as a client, open a connection to a +// server. + +bool FGSocket::open( FGProtocol::fgProtocolDir dir ) { + if ( port_str == "" || port_str == "any" ) { + port = 0; + } else { + port = atoi( port_str.c_str() ); + } + + client_connections.clear(); + + if ( dir == FGProtocol::out ) { + // 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(); + cout << "socket is connected to port = " << port << endl; + + // Specify the maximum length of the connection queue + listen(sock, FG_MAX_SOCKET_QUEUE); + + } else if ( dir == FGProtocol::in ) { + // this means client for now + + sock = make_client_socket(); + } else { + FG_LOG( FG_IO, FG_ALERT, + "Error: bidirection mode not available yet for sockets." ); + return false; + } + + if ( sock < 0 ) { + FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname + << ":" << port ); + return false; + } + + return true; +} + + +// read data from socket (client) +// read a block of data of specified size +int FGSocket::read( char *buf, int length ) { + 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 read on sock (returning immediately, even if + // nothing) + select(32, &ready, 0, 0, &tv); + + if ( FD_ISSET(sock, &ready) ) { + result = std::read( sock, buf, length ); + if ( result != length ) { + FG_LOG( FG_IO, FG_INFO, + "Warning: read() not enough bytes." ); + } + } + + return result; +} + + +// read a line of data, length is max size of input buffer +int FGSocket::readline( char *buf, int length ) { + 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 read on sock (returning immediately, even if + // nothing) + select(32, &ready, 0, 0, &tv); + + if ( FD_ISSET(sock, &ready) ) { + // read a chunk, keep in the save buffer until we have the + // requested amount read + + char *buf_ptr = save_buf + save_len; + result = std::read( sock, buf_ptr, FG_MAX_MSG_SIZE - save_len ); + save_len += result; + + // look for the end of line in save_buf + int 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 + return 0; + } + + // we found an end of line + + // copy to external buffer + strncpy( buf, save_buf, result ); + buf[result] = '\0'; + cout << "fg_serial line = " << buf << endl; + + // shift save buffer + for ( i = result; i < save_len; ++i ) { + save_buf[ i - result ] = save_buf[i]; + } + save_len -= result; + } + + return result; +} + + +// write data to socket (server) +int FGSocket::write( char *buf, int length ) { + + // 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 ) { + FG_LOG( FG_IO, FG_ALERT, + "Error: accept() failed in write()" ); + return 0; + } else { + client_connections.push_back( msgsock ); + } + } + + bool error_condition = false; + for ( int i = 0; i < (int)client_connections.size(); ++i ) { + int msgsock = client_connections[i]; + if ( std::write(msgsock, buf, length) < 0 ) { + FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port ); + error_condition = true; + } + } + + if ( error_condition ) { + return 0; + } + + return length; +} + + +// close the port +bool FGSocket::close() { + for ( int i = 0; i < (int)client_connections.size(); ++i ) { + int msgsock = client_connections[i]; + std::close( msgsock ); + } + + std::close( sock ); + + return true; +} diff --git a/src/Network/fg_socket.hxx b/src/Network/fg_socket.hxx new file mode 100644 index 000000000..7b96bd5a6 --- /dev/null +++ b/src/Network/fg_socket.hxx @@ -0,0 +1,95 @@ +// fg_socket.hxx -- Socket I/O routines +// +// Written by Curtis Olson, started November 1999. +// +// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#ifndef _FG_SOCKET_HXX +#define _FG_SOCKET_HXX + + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include + +#include + +#include + +#include "iochannel.hxx" +#include "protocol.hxx" + +FG_USING_STD(string); + + +#define FG_MAX_SOCKET_QUEUE 32 + + +class FGSocket : public FGIOChannel { + + string hostname; + string port_str; + + char save_buf[ 2 * FG_MAX_MSG_SIZE ]; + int save_len; + + int sock; + short unsigned int port; + + // make a server (master listening) socket + int make_server_socket(); + + // make a client socket + int make_client_socket(); + + int_list client_connections; + +public: + + FGSocket(); + ~FGSocket(); + + // open the file based on specified direction + bool open( FGProtocol::fgProtocolDir dir ); + + // read data from file + int read( char *buf, int length ); + + // read data from file + int readline( char *buf, int length ); + + // write data to a file + int write( char *buf, int length ); + + // close file + bool close(); + + inline string get_hostname() const { return hostname; } + inline void set_hostname( const string& hn ) { hostname = hn; } + inline string get_port_str() const { return port_str; } + inline void set_port_str( const string& p ) { port_str = p; } +}; + + +#endif // _FG_SOCKET_HXX + + diff --git a/src/Network/garmin.cxx b/src/Network/garmin.cxx index 81b279f04..6a0814b72 100644 --- a/src/Network/garmin.cxx +++ b/src/Network/garmin.cxx @@ -154,11 +154,11 @@ bool FGGarmin::gen_message() { // parse Garmin message bool FGGarmin::parse_message() { - FG_LOG( FG_IO, FG_DEBUG, "parse garmin message" ); + FG_LOG( FG_IO, FG_INFO, "parse garmin message" ); string msg = buf; msg = msg.substr( 0, length ); - FG_LOG( FG_IO, FG_DEBUG, "entire message = " << msg ); + FG_LOG( FG_IO, FG_INFO, "entire message = " << msg ); string::size_type begin_line, end_line, begin, end; begin_line = begin = 0; @@ -168,12 +168,12 @@ bool FGGarmin::parse_message() { while ( end_line != string::npos ) { string line = msg.substr(begin_line, end_line - begin_line); begin_line = end_line + 1; - FG_LOG( FG_IO, FG_DEBUG, " input line = " << line ); + FG_LOG( FG_IO, FG_INFO, " input line = " << line ); // leading character string start = msg.substr(begin, 1); ++begin; - FG_LOG( FG_IO, FG_DEBUG, " start = " << start ); + FG_LOG( FG_IO, FG_INFO, " start = " << start ); // sentence end = msg.find(",", begin); @@ -183,7 +183,7 @@ bool FGGarmin::parse_message() { string sentence = msg.substr(begin, end - begin); begin = end + 1; - FG_LOG( FG_IO, FG_DEBUG, " sentence = " << sentence ); + FG_LOG( FG_IO, FG_INFO, " sentence = " << sentence ); double lon_deg, lon_min, lat_deg, lat_min; double lon, lat, speed, heading, altitude; @@ -197,7 +197,7 @@ bool FGGarmin::parse_message() { string utc = msg.substr(begin, end - begin); begin = end + 1; - FG_LOG( FG_IO, FG_DEBUG, " utc = " << utc ); + FG_LOG( FG_IO, FG_INFO, " utc = " << utc ); // junk end = msg.find(",", begin); @@ -207,7 +207,7 @@ bool FGGarmin::parse_message() { string junk = msg.substr(begin, end - begin); begin = end + 1; - FG_LOG( FG_IO, FG_DEBUG, " junk = " << junk ); + FG_LOG( FG_IO, FG_INFO, " junk = " << junk ); // lat val end = msg.find(",", begin); @@ -236,7 +236,7 @@ bool FGGarmin::parse_message() { } cur_fdm_state->set_Latitude( lat * DEG_TO_RAD ); - FG_LOG( FG_IO, FG_DEBUG, " lat = " << lat ); + FG_LOG( FG_IO, FG_INFO, " lat = " << lat ); // lon val end = msg.find(",", begin); @@ -265,7 +265,7 @@ bool FGGarmin::parse_message() { } cur_fdm_state->set_Longitude( lon * DEG_TO_RAD ); - FG_LOG( FG_IO, FG_DEBUG, " lon = " << lon ); + FG_LOG( FG_IO, FG_INFO, " lon = " << lon ); double sl_radius, lat_geoc; fgGeodToGeoc( cur_fdm_state->get_Latitude(), @@ -286,7 +286,7 @@ bool FGGarmin::parse_message() { speed = atof( speed_str.c_str() ); cur_fdm_state->set_V_equiv_kts( speed ); cur_fdm_state->set_V_ground_speed( speed ); - FG_LOG( FG_IO, FG_DEBUG, " speed = " << speed ); + FG_LOG( FG_IO, FG_INFO, " speed = " << speed ); // heading end = msg.find(",", begin); @@ -300,7 +300,7 @@ bool FGGarmin::parse_message() { cur_fdm_state->set_Euler_Angles( cur_fdm_state->get_Phi(), cur_fdm_state->get_Theta(), heading * DEG_TO_RAD ); - FG_LOG( FG_IO, FG_DEBUG, " heading = " << heading ); + FG_LOG( FG_IO, FG_INFO, " heading = " << heading ); } else if ( sentence == "PGRMZ" ) { // altitude end = msg.find(",", begin); @@ -327,7 +327,7 @@ bool FGGarmin::parse_message() { cur_fdm_state->set_Altitude( altitude ); - FG_LOG( FG_IO, FG_DEBUG, " altitude = " << altitude ); + FG_LOG( FG_IO, FG_INFO, " altitude = " << altitude ); } @@ -373,14 +373,24 @@ bool FGGarmin::process() { return false; } } else if ( get_direction() == in ) { - if ( io->readline( buf, FG_MAX_MSG_SIZE ) ) { - parse_message(); + if ( length = io->readline( buf, FG_MAX_MSG_SIZE ) ) { + FG_LOG( FG_IO, FG_ALERT, "Success reading data." ); + if ( parse_message() ) { + FG_LOG( FG_IO, FG_ALERT, "Success parsing data." ); + } else { + FG_LOG( FG_IO, FG_ALERT, "Error parsing data." ); + } } else { FG_LOG( FG_IO, FG_ALERT, "Error reading data." ); return false; } - if ( io->readline( buf, FG_MAX_MSG_SIZE ) ) { - parse_message(); + if ( length = io->readline( buf, FG_MAX_MSG_SIZE ) ) { + FG_LOG( FG_IO, FG_ALERT, "Success reading data." ); + if ( parse_message() ) { + FG_LOG( FG_IO, FG_ALERT, "Success parsing data." ); + } else { + FG_LOG( FG_IO, FG_ALERT, "Error parsing data." ); + } } else { FG_LOG( FG_IO, FG_ALERT, "Error reading data." ); return false; diff --git a/src/Network/nmea.cxx b/src/Network/nmea.cxx index b1755f2e2..7eeb98827 100644 --- a/src/Network/nmea.cxx +++ b/src/Network/nmea.cxx @@ -476,13 +476,13 @@ bool FGNMEA::process() { return false; } } else if ( get_direction() == in ) { - if ( io->readline( buf, FG_MAX_MSG_SIZE ) ) { + if ( length = io->readline( buf, FG_MAX_MSG_SIZE ) ) { parse_message(); } else { FG_LOG( FG_IO, FG_ALERT, "Error reading data." ); return false; } - if ( io->readline( buf, FG_MAX_MSG_SIZE ) ) { + if ( length = io->readline( buf, FG_MAX_MSG_SIZE ) ) { parse_message(); } else { FG_LOG( FG_IO, FG_ALERT, "Error reading data." ); -- 2.39.5