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