From: James Turner Date: Sat, 23 Oct 2010 19:07:35 +0000 (+0100) Subject: Migrate relevant PLIB netXXX classes into SimGear. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=bfd76880a9ca297d308a7a8c5b3ee154ab1187d2;p=simgear.git Migrate relevant PLIB netXXX classes into SimGear. --- diff --git a/projects/VC90/SimGear.vcproj b/projects/VC90/SimGear.vcproj index d0f32f1c..cdfeb222 100644 --- a/projects/VC90/SimGear.vcproj +++ b/projects/VC90/SimGear.vcproj @@ -503,6 +503,12 @@ RelativePath="..\..\simgear\io\sg_socket_udp.hxx" > + + + + + + + +namespace simgear { + +NetBuffer::NetBuffer( int _max_length ) +{ + length = 0 ; + max_length = _max_length ; + data = new char [ max_length+1 ] ; //for null terminator +} + +NetBuffer::~NetBuffer () +{ + delete[] data ; +} + +void NetBuffer::remove () +{ + length = 0 ; +} + +void NetBuffer::remove (int pos, int n) +{ + assert (pos>=0 && pos=0 && pos 0) + { + in_buffer.append (num_read) ; + //ulSetError ( UL_DEBUG, "netBufferChannel: %d read", num_read ) ; + } + } + if (in_buffer.getLength()) + { + handleBufferRead (in_buffer); + } +} + +void +NetBufferChannel::handleWrite (void) +{ + if (out_buffer.getLength()) + { + if (isConnected()) + { + int length = out_buffer.getLength() ; + if (length>512) + length=512; + int num_sent = NetChannel::send ( + out_buffer.getData(), length); + if (num_sent > 0) + { + out_buffer.remove (0, num_sent); + //ulSetError ( UL_DEBUG, "netBufferChannel: %d sent", num_sent ) ; + } + } + } + else if (should_close) + { + close(); + } +} + +} // of namespace simgear diff --git a/simgear/io/sg_netBuffer.hxx b/simgear/io/sg_netBuffer.hxx new file mode 100644 index 00000000..bd180687 --- /dev/null +++ b/simgear/io/sg_netBuffer.hxx @@ -0,0 +1,155 @@ +/* + Copied from PLIB into SimGear + + 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 + + For further information visit http://plib.sourceforge.net + + $Id: netBuffer.h 1901 2004-03-21 18:19:11Z sjbaker $ +*/ + +/**** +* NAME +* netBuffer - network buffer class +* +* DESCRIPTION +* Clients and servers built on top of netBufferChannel +* automatically support pipelining. +* +* Pipelining refers to a protocol capability. Normally, +* a conversation with a server has a back-and-forth +* quality to it. The client sends a command, and +* waits for the response. If a client needs to send +* many commands over a high-latency connection, +* waiting for each response can take a long time. +* +* For example, when sending a mail message to many recipients +* with SMTP, the client will send a series of RCPT commands, one +* for each recipient. For each of these commands, the server will +* send back a reply indicating whether the mailbox specified is +* valid. If you want to send a message to several hundred recipients, +* this can be rather tedious if the round-trip time for each command +* is long. You'd like to be able to send a bunch of RCPT commands +* in one batch, and then count off the responses to them as they come. +* +* I have a favorite visual when explaining the advantages of +* pipelining. Imagine each request to the server is a boxcar on a train. +* The client is in Los Angeles, and the server is in New York. +* Pipelining lets you hook all your cars in one long chain; send +* them to New York, where they are filled and sent back to you. +* Without pipelining you have to send one car at a time. +* +* Not all protocols allow pipelining. Not all servers support it; +* Sendmail, for example, does not support pipelining because it tends +* to fork unpredictably, leaving buffered data in a questionable state. +* A recent extension to the SMTP protocol allows a server to specify +* whether it supports pipelining. HTTP/1.1 explicitly requires that +* a server support pipelining. +* +* NOTES +* When a user passes in a buffer object, it belongs to +* the user. When the library gives a buffer to the user, +* the user should copy it. +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef SG_NET_BUFFER_H +#define SG_NET_BUFFER_H + +#include + +namespace simgear +{ + +// =========================================================================== +// NetBuffer +// =========================================================================== + +class NetBuffer +{ +protected: + int length ; + int max_length ; + char* data ; + +public: + NetBuffer( int _max_length ); + ~NetBuffer (); + int getLength() const { return length ; } + int getMaxLength() const { return max_length ; } + + /* + ** getData() returns a pointer to the data + ** Note: a zero (0) byte is appended for convenience + ** but the data may have internal zero (0) bytes already + */ + char* getData() { data [length] = 0 ; return data ; } + const char* getData() const { ((char*)data) [length] = 0 ; return data ; } + + void remove (); + void remove (int pos, int n); + bool append (const char* s, int n); + bool append (int n); +}; + +// =========================================================================== +// NetBufferChannel +// =========================================================================== + +class NetBufferChannel : public NetChannel +{ + NetBuffer in_buffer; + NetBuffer out_buffer; + int should_close ; + + virtual bool readable (void) + { + return (NetChannel::readable() && + (in_buffer.getLength() < in_buffer.getMaxLength())); + } + + virtual void handleRead (void) ; + + virtual bool writable (void) + { + return (out_buffer.getLength() || should_close); + } + + virtual void handleWrite (void) ; + +public: + + NetBufferChannel (int in_buffer_size = 4096, int out_buffer_size = 16384); + virtual void handleClose ( void ); + + void closeWhenDone (void) { should_close = 1 ; } + + virtual bool bufferSend (const char* msg, int msg_len); + virtual void handleBufferRead (NetBuffer& buffer); +}; + +} // namespace simgear + +#endif // SG_NET_BUFFER_H diff --git a/simgear/io/sg_netChannel.cxx b/simgear/io/sg_netChannel.cxx new file mode 100644 index 00000000..e02f073b --- /dev/null +++ b/simgear/io/sg_netChannel.cxx @@ -0,0 +1,305 @@ +/* + Copied from PLIB into SimGear + + 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 + + For further information visit http://plib.sourceforge.net + + $Id: netChannel.cxx 1906 2004-03-22 19:44:50Z sjbaker $ +*/ + +// TODO: +// have all socket-related functions assert that the socket has not +// been closed. [a read event may close it, and a write event may try +// to write or something...] +// Maybe assert valid handle, too? + +#include "sg_netChannel.hxx" + +#include + +#include + +namespace simgear { + +static NetChannel* channels = 0 ; + +NetChannel::NetChannel () +{ + closed = true ; + connected = false ; + accepting = false ; + write_blocked = false ; + should_delete = false ; + + next_channel = channels ; + channels = this ; +} + +NetChannel::~NetChannel () +{ + close(); + + NetChannel* prev = NULL ; + + for ( NetChannel* ch = channels; ch != NULL; + ch = ch -> next_channel ) + { + if (ch == this) + { + ch = ch -> next_channel ; + if ( prev != NULL ) + prev -> next_channel = ch ; + else + channels = ch ; + next_channel = 0 ; + break; + } + prev = ch ; + } +} + +void +NetChannel::setHandle (int handle, bool is_connected) +{ + close () ; + Socket::setHandle ( handle ) ; + connected = is_connected ; + //if ( connected ) this->handleConnect(); + closed = false ; +} + +bool +NetChannel::open (void) +{ + close(); + if (Socket::open(true)) { + closed = false ; + setBlocking ( false ) ; + return true ; + } + return false ; +} + +int +NetChannel::listen ( int backlog ) +{ + accepting = true ; + return Socket::listen ( backlog ) ; +} + +int +NetChannel::connect ( const char* host, int port ) +{ + int result = Socket::connect ( host, port ) ; + if (result == 0) { + connected = true ; + //this->handleConnect(); + return 0; + } else if (isNonBlockingError ()) { + return 0; + } else { + // some other error condition + this->handleError (result); + close(); + return -1; + } +} + +int +NetChannel::send (const void * buffer, int size, int flags) +{ + int result = Socket::send (buffer, size, flags); + + if (result == (int)size) { + // everything was sent + write_blocked = false ; + return result; + } else if (result >= 0) { + // not all of it was sent, but no error + write_blocked = true ; + return result; + } else if (isNonBlockingError ()) { + write_blocked = true ; + return 0; + } else { + this->handleError (result); + close(); + return -1; + } + +} + +int +NetChannel::recv (void * buffer, int size, int flags) +{ + int result = Socket::recv (buffer, size, flags); + + if (result > 0) { + return result; + } else if (result == 0) { + close(); + return 0; + } else if (isNonBlockingError ()) { + return 0; + } else { + this->handleError (result); + close(); + return -1; + } +} + +void +NetChannel::close (void) +{ + if ( !closed ) + { + this->handleClose(); + + closed = true ; + connected = false ; + accepting = false ; + write_blocked = false ; + } + + Socket::close () ; +} + +void +NetChannel::handleReadEvent (void) +{ + if (accepting) { + if (!connected) { + connected = true ; + //this->handleConnect(); + } + this->handleAccept(); + } else if (!connected) { + connected = true ; + //this->handleConnect(); + this->handleRead(); + } else { + this->handleRead(); + } +} + +void +NetChannel::handleWriteEvent (void) +{ + if (!connected) { + connected = true ; + //this->handleConnect(); + } + write_blocked = false ; + this->handleWrite(); +} + +bool +NetChannel::poll (unsigned int timeout) +{ + if (!channels) + return false ; + + enum { MAX_SOCKETS = 256 } ; + Socket* reads [ MAX_SOCKETS+1 ] ; + Socket* writes [ MAX_SOCKETS+1 ] ; + Socket* deletes [ MAX_SOCKETS+1 ] ; + int nreads = 0 ; + int nwrites = 0 ; + int ndeletes = 0 ; + int nopen = 0 ; + NetChannel* ch; + for ( ch = channels; ch != NULL; ch = ch -> next_channel ) + { + if ( ch -> should_delete ) + { + assert(ndeletes closed ) + { + nopen++ ; + if (ch -> readable()) { + assert(nreads writable()) { + assert(nwrites closed ) + ch -> handleReadEvent(); + } + + for ( i=0; writes[i]; i++ ) + { + ch = (NetChannel*)writes[i]; + if ( ! ch -> closed ) + ch -> handleWriteEvent(); + } + + return true ; +} + +void +NetChannel::loop (unsigned int timeout) +{ + while ( poll (timeout) ) ; +} + + +void NetChannel::handleRead (void) { + SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled read"); +} + +void NetChannel::handleWrite (void) { + SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled write"); +} + +void NetChannel::handleAccept (void) { + SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled accept"); +} + +void NetChannel::handleError (int error) { + SG_LOG(SG_IO, SG_WARN,"Network:" << getHandle() << ": errno: " << strerror(errno) <<"(" << errno << ")"); +} + +} // of namespace simgear diff --git a/simgear/io/sg_netChannel.hxx b/simgear/io/sg_netChannel.hxx new file mode 100644 index 00000000..30af5122 --- /dev/null +++ b/simgear/io/sg_netChannel.hxx @@ -0,0 +1,115 @@ +/* + NetChannel - copied from PLIB + + 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 + + For further information visit http://plib.sourceforge.net + + $Id: netChannel.h 1899 2004-03-21 17:41:07Z sjbaker $ +*/ + +/**** +* NAME +* netChannel - network channel class +* +* DESCRIPTION +* netChannel is adds event-handling to the low-level +* netSocket class. Otherwise, it can be treated as +* a normal non-blocking socket object. +* +* The direct interface between the netPoll() loop and +* the channel object are the handleReadEvent and +* handleWriteEvent methods. These are called +* whenever a channel object 'fires' that event. +* +* The firing of these low-level events can tell us whether +* certain higher-level events have taken place, depending on +* the timing and state of the connection. +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef SG_NET_CHANNEL_H +#define SG_NET_CHANNEL_H + +#include + +namespace simgear +{ + +class NetChannel : public Socket +{ + bool closed, connected, accepting, write_blocked, should_delete ; + NetChannel* next_channel ; + + friend bool netPoll (unsigned int timeout); + +public: + + NetChannel () ; + virtual ~NetChannel () ; + + void setHandle (int s, bool is_connected = true); + bool isConnected () const { return connected; } + bool isClosed () const { return closed; } + void shouldDelete () { should_delete = true ; } + + // -------------------------------------------------- + // socket methods + // -------------------------------------------------- + + bool open ( void ) ; + void close ( void ) ; + int listen ( int backlog ) ; + int connect ( const char* host, int port ) ; + int send ( const void * buf, int size, int flags = 0 ) ; + int recv ( void * buf, int size, int flags = 0 ) ; + + // poll() eligibility predicates + virtual bool readable (void) { return (connected || accepting); } + virtual bool writable (void) { return (!connected || write_blocked); } + + // -------------------------------------------------- + // event handlers + // -------------------------------------------------- + + void handleReadEvent (void); + void handleWriteEvent (void); + +// These are meant to be overridden. + virtual void handleClose (void) { + } + + virtual void handleRead (void); + virtual void handleWrite (void); + virtual void handleAccept (void); + virtual void handleError (int error); + + static bool poll (unsigned int timeout = 0 ) ; + static void loop (unsigned int timeout = 0 ) ; +}; + +} // of namespace simgear + +#endif // SG_NET_CHANNEL_H diff --git a/simgear/io/sg_netChat.cxx b/simgear/io/sg_netChat.cxx new file mode 100644 index 00000000..fcced48e --- /dev/null +++ b/simgear/io/sg_netChat.cxx @@ -0,0 +1,138 @@ +/* + Copied from PLIB into SimGear + + 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 + + For further information visit http://plib.sourceforge.net + + $Id: NetChat.cxx 2117 2007-09-13 23:21:09Z fayjf $ +*/ + +#include + +#include // for strdup + +namespace simgear { + +void +NetChat::setTerminator (const char* t) +{ + if (terminator) delete[] terminator; + terminator = strdup(t); +} + +const char* +NetChat::getTerminator (void) +{ + return terminator; +} + +// return the size of the largest prefix of needle at the end +// of haystack + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +static int +find_prefix_at_end (const NetBuffer& haystack, const char* needle) +{ + const char* hd = haystack.getData(); + int hl = haystack.getLength(); + int nl = strlen(needle); + + for (int i = MAX (nl-hl, 0); i < nl; i++) { + //if (haystack.compare (needle, hl-(nl-i), nl-i) == 0) { + if (memcmp(needle, &hd[hl-(nl-i)], nl-i) == 0) { + return (nl-i); + } + } + return 0; +} + +static int +find_terminator (const NetBuffer& haystack, const char* needle) +{ + if (needle && *needle) + { + const char* data = haystack.getData(); + const char* ptr = strstr(data,needle); + if (ptr != NULL) + return(ptr-data); + } + return -1; +} + +bool NetChat::push (const char* s) +{ + return bufferSend ( s, strlen(s) ) ; +} + +void +NetChat::handleBufferRead (NetBuffer& in_buffer) +{ + // Continue to search for terminator in in_buffer, + // while calling collect_incoming_data. The while loop is + // necessary because we might read several data+terminator combos + // with a single recv(). + + while (in_buffer.getLength()) { + + // special case where we're not using a terminator + if (terminator == 0 || *terminator == 0) { + collectIncomingData (in_buffer.getData(),in_buffer.getLength()); + in_buffer.remove (); + return; + } + + int terminator_len = strlen(terminator); + + int index = find_terminator ( in_buffer, terminator ) ; + + // 3 cases: + // 1) end of buffer matches terminator exactly: + // collect data, transition + // 2) end of buffer matches some prefix: + // collect data to the prefix + // 3) end of buffer does not match any prefix: + // collect data + + if (index != -1) { + // we found the terminator + collectIncomingData ( in_buffer.getData(), index ) ; + in_buffer.remove (0, index + terminator_len); + foundTerminator(); + } else { + // check for a prefix of the terminator + int num = find_prefix_at_end (in_buffer, terminator); + if (num) { + int bl = in_buffer.getLength(); + // we found a prefix, collect up to the prefix + collectIncomingData ( in_buffer.getData(), bl - num ) ; + in_buffer.remove (0, bl - num); + break; + } else { + // no prefix, collect it all + collectIncomingData (in_buffer.getData(), in_buffer.getLength()); + in_buffer.remove(); + } + } + } +} + +} // of namespace simgear + + diff --git a/simgear/io/sg_netChat.hxx b/simgear/io/sg_netChat.hxx new file mode 100644 index 00000000..e9ab2e03 --- /dev/null +++ b/simgear/io/sg_netChat.hxx @@ -0,0 +1,90 @@ +/* + Copied from PLIB into SimGear + + 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 + + For further information visit http://plib.sourceforge.net + + $Id: netChat.h 1568 2002-09-02 06:05:49Z sjbaker $ +*/ + +/**** +* NAME +* netChat - network chat class +* +* DESCRIPTION +* This class adds support for 'chat' style protocols - +* where one side sends a 'command', and the other sends +* a response (examples would be the common internet +* protocols - smtp, nntp, ftp, etc..). +* +* The handle_buffer_read() method looks at the input +* stream for the current 'terminator' (usually '\r\n' +* for single-line responses, '\r\n.\r\n' for multi-line +* output), calling found_terminator() on its receipt. +* +* EXAMPLE +* Say you build an nntp client using this class. +* At the start of the connection, you'll have +* terminator set to '\r\n', in order to process +* the single-line greeting. Just before issuing a +* 'LIST' command you'll set it to '\r\n.\r\n'. +* The output of the LIST command will be accumulated +* (using your own 'collect_incoming_data' method) +* up to the terminator, and then control will be +* returned to you - by calling your found_terminator() +* +* AUTHORS +* Sam Rushing - original version for Medusa +* Dave McClurg - modified for use in PLIB +* +* CREATION DATE +* Dec-2000 +* +****/ + +#ifndef SG_NET_CHAT_H +#define SG_NET_CHAT_H + +#include + +namespace simgear +{ + +class NetChat : public NetBufferChannel +{ + char* terminator; + + virtual void handleBufferRead (NetBuffer& buffer) ; + +public: + + NetChat () : terminator (0) {} + + void setTerminator (const char* t); + const char* getTerminator (void); + + bool push (const char* s); + + virtual void collectIncomingData (const char* s, int n) {} + virtual void foundTerminator (void) {} +}; + +} // of namespace simgear + +#endif // SG_NET_CHAT_H