2 Copied from PLIB into SimGear
4 PLIB - A Suite of Portable Game Libraries
5 Copyright (C) 1998,2002 Steve Baker
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 For further information visit http://plib.sourceforge.net
23 $Id: netChannel.cxx 1906 2004-03-22 19:44:50Z sjbaker $
27 // have all socket-related functions assert that the socket has not
28 // been closed. [a read event may close it, and a write event may try
29 // to write or something...]
30 // Maybe assert valid handle, too?
32 #include "sg_netChannel.hxx"
39 #include <simgear/debug/logstream.hxx>
44 NetChannel::NetChannel ()
48 resolving_host = false;
50 write_blocked = false ;
51 should_delete = false ;
55 NetChannel::~NetChannel ()
59 poller->removeChannel(this);
64 NetChannel::setHandle (int handle, bool is_connected)
67 Socket::setHandle ( handle ) ;
68 connected = is_connected ;
73 NetChannel::open (void)
76 if (Socket::open(true)) {
78 setBlocking ( false ) ;
85 NetChannel::listen ( int backlog )
88 return Socket::listen ( backlog ) ;
92 NetChannel::connect ( const char* h, int p )
96 resolving_host = true;
97 return handleResolve();
101 NetChannel::send (const void * buffer, int size, int flags)
103 int result = Socket::send (buffer, size, flags);
105 if (result == (int)size) {
106 // everything was sent
107 write_blocked = false ;
109 } else if (result >= 0) {
110 // not all of it was sent, but no error
111 write_blocked = true ;
113 } else if (isNonBlockingError ()) {
114 write_blocked = true ;
117 this->handleError (result);
125 NetChannel::recv (void * buffer, int size, int flags)
127 int result = Socket::recv (buffer, size, flags);
131 } else if (result == 0) {
134 } else if (isNonBlockingError ()) {
137 this->handleError (result);
144 NetChannel::close (void)
153 write_blocked = false ;
160 NetChannel::handleReadEvent (void)
166 this->handleAccept();
167 } else if (!connected) {
176 NetChannel::handleWriteEvent (void)
181 write_blocked = false ;
186 NetChannel::handleResolve()
189 if (!IPAddress::lookupNonblocking(host.c_str(), addr)) {
190 return 0; // not looked up yet, wait longer
193 if (!addr.isValid()) {
194 SG_LOG(SG_IO, SG_WARN, "Network: host lookup failed:" << host);
195 handleError (ENOENT);
200 resolving_host = false;
202 int result = Socket::connect ( &addr ) ;
206 } else if (isNonBlockingError ()) {
209 // some other error condition
210 handleError (result);
216 void NetChannel::handleRead (void) {
217 SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled read");
220 void NetChannel::handleWrite (void) {
221 SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled write");
224 void NetChannel::handleAccept (void) {
225 SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled accept");
228 void NetChannel::handleError (int error)
230 if (error == EINPROGRESS) {
231 // this shoudl never happen, because we should use isNonBlocking to check
233 SG_LOG(SG_IO, SG_WARN, "Got EINPROGRESS at NetChannel::handleError: suggests broken logic somewhere else");
234 return; // not an actual error, don't warn
237 // warn about address lookup failures seperately, don't warn again.
238 // (and we (ab-)use ENOENT to mean 'name not found'.
239 if (error != ENOENT) {
240 SG_LOG(SG_IO, SG_WARN,"Network:" << getHandle() << ": errno: " << strerror(errno) <<"(" << errno << ")");
245 NetChannelPoller::addChannel(NetChannel* channel)
248 assert(channel->poller == NULL);
250 channel->poller = this;
251 channels.push_back(channel);
255 NetChannelPoller::removeChannel(NetChannel* channel)
258 assert(channel->poller == this);
259 channel->poller = NULL;
261 // portability: MSVC throws assertion failure when empty
262 if (channels.empty()) {
266 ChannelList::iterator it = channels.begin();
267 for (; it != channels.end(); ++it) {
268 if (*it == channel) {
276 NetChannelPoller::poll(unsigned int timeout)
278 if (channels.empty()) {
282 enum { MAX_SOCKETS = 256 } ;
283 Socket* reads [ MAX_SOCKETS+1 ] ;
284 Socket* writes [ MAX_SOCKETS+1 ] ;
289 ChannelList::iterator it = channels.begin();
290 while( it != channels.end() )
292 NetChannel* ch = *it;
293 if ( ch -> should_delete )
295 // avoid the channel trying to remove itself from us, or we get
296 // bug http://code.google.com/p/flightgear-bugs/issues/detail?id=1144
299 it = channels.erase(it);
303 ++it; // we've copied the pointer into ch
308 if (ch -> resolving_host )
310 ch -> handleResolve();
315 if (ch -> readable()) {
316 assert(nreads<MAX_SOCKETS);
317 reads[nreads++] = ch ;
319 if (ch -> writable()) {
320 assert(nwrites<MAX_SOCKETS);
321 writes[nwrites++] = ch ;
323 } // of array-filling pass
325 reads[nreads] = NULL ;
326 writes[nwrites] = NULL ;
330 if (!nreads && !nwrites)
331 return true ; //hmmm- should we shutdown?
333 Socket::select (reads, writes, timeout) ;
335 for ( int i=0; reads[i]; i++ )
337 NetChannel* ch = (NetChannel*)reads[i];
338 if ( ! ch -> closed )
339 ch -> handleReadEvent();
342 for ( int i=0; writes[i]; i++ )
344 NetChannel* ch = (NetChannel*)writes[i];
345 if ( ! ch -> closed )
346 ch -> handleWriteEvent();
353 NetChannelPoller::loop (unsigned int timeout)
355 while ( poll (timeout) ) ;
359 } // of namespace simgear