]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_netChannel.cxx
Unit Test: Fixed failure of test_HTTP
[simgear.git] / simgear / io / sg_netChannel.cxx
1 /*
2   Copied from PLIB into SimGear
3   
4      PLIB - A Suite of Portable Game Libraries
5      Copyright (C) 1998,2002  Steve Baker
6  
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.
11  
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.
16  
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
20  
21      For further information visit http://plib.sourceforge.net
22
23      $Id: netChannel.cxx 1906 2004-03-22 19:44:50Z sjbaker $
24 */
25
26 // TODO:
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?
31
32 #include "sg_netChannel.hxx"
33
34 #include <memory>
35 #include <cassert>
36 #include <cstring>
37 #include <errno.h>
38
39 #include <simgear/debug/logstream.hxx>
40
41
42 namespace simgear  {
43
44 NetChannel::NetChannel ()
45 {
46   closed = true ;
47   connected = false ;
48   resolving_host  = false;
49   accepting = false ;
50   write_blocked = false ;
51   should_delete = false ;
52   poller = NULL;
53 }
54   
55 NetChannel::~NetChannel ()
56 {
57   close();
58   if (poller) {
59       poller->removeChannel(this);
60   }
61 }
62   
63 void
64 NetChannel::setHandle (int handle, bool is_connected)
65 {
66   close () ;
67   Socket::setHandle ( handle ) ;
68   connected = is_connected ;
69   closed = false ;
70 }
71
72 bool
73 NetChannel::open (void)
74 {
75   close();
76   if (Socket::open(true)) {
77     closed = false ;
78     setBlocking ( false ) ;
79     return true ;
80   }
81   return false ;
82 }
83
84 int
85 NetChannel::listen ( int backlog )
86 {
87   accepting = true ;
88   return Socket::listen ( backlog ) ;
89 }
90
91 int
92 NetChannel::connect ( const char* h, int p )
93 {
94   host = h;
95   port = p;
96   resolving_host = true;
97   return handleResolve();
98 }
99
100 int
101 NetChannel::send (const void * buffer, int size, int flags)
102 {
103   int result = Socket::send (buffer, size, flags);
104   
105   if (result == (int)size) {
106     // everything was sent
107     write_blocked = false ;
108     return result;
109   } else if (result >= 0) {
110     // not all of it was sent, but no error
111     write_blocked = true ;
112     return result;
113   } else if (isNonBlockingError ()) {
114     write_blocked = true ;
115     return 0;
116   } else {
117     this->handleError (result);
118     close();
119     return -1;
120   }
121   
122 }
123
124 int
125 NetChannel::recv (void * buffer, int size, int flags)
126 {
127   int result = Socket::recv (buffer, size, flags);
128   
129   if (result > 0) {
130     return result;
131   } else if (result == 0) {
132     close();
133     return 0;
134   } else if (isNonBlockingError ()) {
135     return 0;
136   } else {
137     this->handleError (result);
138     close();
139     return -1;
140   }
141 }
142
143 void
144 NetChannel::close (void)
145 {
146   if ( !closed )
147   {
148     this->handleClose();
149   
150     closed = true ;
151     connected = false ;
152     accepting = false ;
153     write_blocked = false ;
154   }
155
156   Socket::close () ;
157 }
158
159 void
160 NetChannel::handleReadEvent (void)
161 {
162   if (accepting) {
163     if (!connected) {
164       connected = true ;
165     }
166     this->handleAccept();
167   } else if (!connected) {
168     connected = true ;
169     this->handleRead();
170   } else {
171     this->handleRead();
172   }
173 }
174
175 void
176 NetChannel::handleWriteEvent (void)
177 {
178   if (!connected) {
179     connected = true ;
180   }
181   write_blocked = false ;
182   this->handleWrite();
183 }
184
185 int
186 NetChannel::handleResolve()
187 {
188     IPAddress addr;
189     if (!IPAddress::lookupNonblocking(host.c_str(), addr)) {
190         return 0; // not looked up yet, wait longer
191     }
192     
193     if (!addr.isValid()) {
194         SG_LOG(SG_IO, SG_WARN, "Network: host lookup failed:" << host);
195         handleError (ENOENT);
196         close();
197         return -1;
198     }
199     
200     resolving_host = false;
201     addr.setPort(port);
202     int result = Socket::connect ( &addr ) ;
203     if (result == 0) {
204         connected = true ;
205         return 0;
206     } else if (isNonBlockingError ()) {
207         return 0;
208     } else {
209         // some other error condition
210         handleError (result);
211         close();
212         return -1;
213     }
214 }
215
216 void NetChannel::handleRead (void) {
217   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled read");
218 }
219
220 void NetChannel::handleWrite (void) {
221   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled write");
222 }
223
224 void NetChannel::handleAccept (void) {
225   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled accept");
226 }
227
228 void NetChannel::handleError (int error)
229 {
230     // warn about address lookup failures seperately, don't warn again.
231     // (and we (ab-)use ENOENT to mean 'name not found'.
232     if (error != ENOENT) {
233         SG_LOG(SG_IO, SG_WARN,"Network:" << getHandle() << ": errno: " << strerror(errno) <<"(" << errno << ")");
234     }
235 }
236
237 void
238 NetChannelPoller::addChannel(NetChannel* channel)
239 {
240     assert(channel);
241     assert(channel->poller == NULL);
242         
243     channel->poller = this;
244     channels.push_back(channel);
245 }
246
247 void
248 NetChannelPoller::removeChannel(NetChannel* channel)
249 {
250     assert(channel);
251     assert(channel->poller == this);
252     channel->poller = NULL;
253     
254     // portability: MSVC throws assertion failure when empty
255     if (channels.empty()) {
256         return;
257     }
258
259     ChannelList::iterator it = channels.begin();
260     for (; it != channels.end(); ++it) {
261         if (*it == channel) {
262             channels.erase(it);
263             return;
264         }
265     }
266 }
267
268 bool
269 NetChannelPoller::poll(unsigned int timeout)
270 {
271     if (channels.empty()) {
272         return false;
273     }
274     
275     enum { MAX_SOCKETS = 256 } ;
276     Socket* reads [ MAX_SOCKETS+1 ] ;
277     Socket* writes [ MAX_SOCKETS+1 ] ;
278     int nreads = 0 ;
279     int nwrites = 0 ;
280     int nopen = 0 ;
281     
282     ChannelList::iterator it = channels.begin();
283     while( it != channels.end() )
284     {
285         NetChannel* ch = *it;
286         if ( ch -> should_delete )
287         {
288             // avoid the channel trying to remove itself from us, or we get
289             // bug http://code.google.com/p/flightgear-bugs/issues/detail?id=1144
290             ch->poller = NULL;
291             delete ch;
292             it = channels.erase(it);
293             continue;
294         }
295
296         ++it; // we've copied the pointer into ch
297         if ( ch->closed ) { 
298             continue;
299         }
300
301         if (ch -> resolving_host )
302         {
303             ch -> handleResolve();
304             continue;
305         }
306       
307         nopen++ ;
308         if (ch -> readable()) {
309           assert(nreads<MAX_SOCKETS);
310           reads[nreads++] = ch ;
311         }
312         if (ch -> writable()) {
313           assert(nwrites<MAX_SOCKETS);
314           writes[nwrites++] = ch ;
315         }
316     } // of array-filling pass
317     
318     reads[nreads] = NULL ;
319     writes[nwrites] = NULL ;
320
321     if (!nopen)
322       return false ;
323     if (!nreads && !nwrites)
324       return true ; //hmmm- should we shutdown?
325
326     Socket::select (reads, writes, timeout) ;
327
328     for ( int i=0; reads[i]; i++ )
329     {
330       NetChannel* ch = (NetChannel*)reads[i];
331       if ( ! ch -> closed )
332         ch -> handleReadEvent();
333     }
334
335     for ( int i=0; writes[i]; i++ )
336     {
337       NetChannel* ch = (NetChannel*)writes[i];
338       if ( ! ch -> closed )
339         ch -> handleWriteEvent();
340     }
341
342     return true ;
343 }
344
345 void
346 NetChannelPoller::loop (unsigned int timeout)
347 {
348   while ( poll (timeout) ) ;
349 }
350
351
352 } // of namespace simgear