]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_netChannel.cxx
Ensure individual log-level setting works.
[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 static NetChannel* channels = 0 ;
45
46 NetChannel::NetChannel ()
47 {
48   closed = true ;
49   connected = false ;
50   resolving_host  = false;
51   accepting = false ;
52   write_blocked = false ;
53   should_delete = false ;
54
55   next_channel = channels ;
56   channels = this ;
57 }
58   
59 NetChannel::~NetChannel ()
60 {
61   close();
62
63   NetChannel* prev = NULL ;
64
65   for ( NetChannel* ch = channels; ch != NULL;
66                     ch = ch -> next_channel )
67   {
68     if (ch == this)
69     {
70       ch = ch -> next_channel ;
71       if ( prev != NULL )
72         prev -> next_channel = ch ;
73       else
74         channels = ch ;
75       next_channel = 0 ;
76       break;
77     }
78     prev = ch ;
79   }
80 }
81   
82 void
83 NetChannel::setHandle (int handle, bool is_connected)
84 {
85   close () ;
86   Socket::setHandle ( handle ) ;
87   connected = is_connected ;
88   closed = false ;
89 }
90
91 bool
92 NetChannel::open (void)
93 {
94   close();
95   if (Socket::open(true)) {
96     closed = false ;
97     setBlocking ( false ) ;
98     return true ;
99   }
100   return false ;
101 }
102
103 int
104 NetChannel::listen ( int backlog )
105 {
106   accepting = true ;
107   return Socket::listen ( backlog ) ;
108 }
109
110 int
111 NetChannel::connect ( const char* h, int p )
112 {
113   host = h;
114   port = p;
115   resolving_host = true;
116   return handleResolve();
117 }
118
119 int
120 NetChannel::send (const void * buffer, int size, int flags)
121 {
122   int result = Socket::send (buffer, size, flags);
123   
124   if (result == (int)size) {
125     // everything was sent
126     write_blocked = false ;
127     return result;
128   } else if (result >= 0) {
129     // not all of it was sent, but no error
130     write_blocked = true ;
131     return result;
132   } else if (isNonBlockingError ()) {
133     write_blocked = true ;
134     return 0;
135   } else {
136     this->handleError (result);
137     close();
138     return -1;
139   }
140   
141 }
142
143 int
144 NetChannel::recv (void * buffer, int size, int flags)
145 {
146   int result = Socket::recv (buffer, size, flags);
147   
148   if (result > 0) {
149     return result;
150   } else if (result == 0) {
151     close();
152     return 0;
153   } else if (isNonBlockingError ()) {
154     return 0;
155   } else {
156     this->handleError (result);
157     close();
158     return -1;
159   }
160 }
161
162 void
163 NetChannel::close (void)
164 {
165   if ( !closed )
166   {
167     this->handleClose();
168   
169     closed = true ;
170     connected = false ;
171     accepting = false ;
172     write_blocked = false ;
173   }
174
175   Socket::close () ;
176 }
177
178 void
179 NetChannel::handleReadEvent (void)
180 {
181   if (accepting) {
182     if (!connected) {
183       connected = true ;
184     }
185     this->handleAccept();
186   } else if (!connected) {
187     connected = true ;
188     this->handleRead();
189   } else {
190     this->handleRead();
191   }
192 }
193
194 void
195 NetChannel::handleWriteEvent (void)
196 {
197   if (!connected) {
198     connected = true ;
199   }
200   write_blocked = false ;
201   this->handleWrite();
202 }
203
204 int
205 NetChannel::handleResolve()
206 {
207     IPAddress addr;
208     if (!IPAddress::lookupNonblocking(host.c_str(), addr)) {
209         return 0; // not looked up yet, wait longer
210     }
211     
212     if (!addr.isValid()) {
213         SG_LOG(SG_IO, SG_WARN, "Network: host lookup failed:" << host);
214         handleError (ENOENT);
215         close();
216         return -1;
217     }
218     
219     resolving_host = false;
220     addr.setPort(port);
221     int result = Socket::connect ( &addr ) ;
222     if (result == 0) {
223         connected = true ;
224         return 0;
225     } else if (isNonBlockingError ()) {
226         return 0;
227     } else {
228         // some other error condition
229         handleError (result);
230         close();
231         return -1;
232     }
233 }
234
235 bool
236 NetChannel::poll (unsigned int timeout)
237 {
238   if (!channels)
239     return false ;
240   
241   enum { MAX_SOCKETS = 256 } ;
242   Socket* reads [ MAX_SOCKETS+1 ] ;
243   Socket* writes [ MAX_SOCKETS+1 ] ;
244   Socket* deletes [ MAX_SOCKETS+1 ] ;
245   int nreads = 0 ;
246   int nwrites = 0 ;
247   int ndeletes = 0 ;
248   int nopen = 0 ;
249   NetChannel* ch;
250   for (  ch = channels; ch != NULL; ch = ch -> next_channel )
251   {
252     if ( ch -> should_delete )
253     {
254       assert(ndeletes<MAX_SOCKETS);
255       deletes[ndeletes++] = ch ;
256     }
257     else if ( ! ch -> closed )
258     {
259       if (ch -> resolving_host )
260       {
261           ch -> handleResolve();
262           continue;
263       }
264       
265       nopen++ ;
266       if (ch -> readable()) {
267         assert(nreads<MAX_SOCKETS);
268         reads[nreads++] = ch ;
269       }
270       if (ch -> writable()) {
271         assert(nwrites<MAX_SOCKETS);
272         writes[nwrites++] = ch ;
273       }
274     }
275   }
276   reads[nreads] = NULL ;
277   writes[nwrites] = NULL ;
278   deletes[ndeletes] = NULL ;
279
280   int i ;
281   for ( i=0; deletes[i]; i++ )
282   {
283     ch = (NetChannel*)deletes[i];
284     delete ch ;
285   }
286
287   if (!nopen)
288     return false ;
289   if (!nreads && !nwrites)
290     return true ; //hmmm- should we shutdown?
291
292   Socket::select (reads, writes, timeout) ;
293
294   for ( i=0; reads[i]; i++ )
295   {
296     ch = (NetChannel*)reads[i];
297     if ( ! ch -> closed )
298       ch -> handleReadEvent();
299   }
300
301   for ( i=0; writes[i]; i++ )
302   {
303     ch = (NetChannel*)writes[i];
304     if ( ! ch -> closed )
305       ch -> handleWriteEvent();
306   }
307
308   return true ;
309 }
310
311 void
312 NetChannel::loop (unsigned int timeout)
313 {
314   while ( poll (timeout) ) ;
315 }
316
317
318 void NetChannel::handleRead (void) {
319   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled read");
320 }
321
322 void NetChannel::handleWrite (void) {
323   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled write");
324 }
325
326 void NetChannel::handleAccept (void) {
327   SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled accept");
328 }
329
330 void NetChannel::handleError (int error)
331 {
332     // warn about address lookup failures seperately, don't warn again.
333     // (and we (ab-)use ENOENT to mean 'name not found'.
334     if (error != ENOENT) {
335         SG_LOG(SG_IO, SG_WARN,"Network:" << getHandle() << ": errno: " << strerror(errno) <<"(" << errno << ")");
336     }
337 }
338
339 } // of namespace simgear