]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_netChannel.cxx
3fe0decfa85ca0aaa1acc1183a5e739835cc7e11
[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 (errorNumber());
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 (errorNumber());
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 (errorNumber());
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     if (error == EINPROGRESS) {
231         // this shoudl never happen, because we should use isNonBlocking to check
232         // such error codes.
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
235     }
236
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 << ")");
241     }
242 }
243
244 void
245 NetChannelPoller::addChannel(NetChannel* channel)
246 {
247     assert(channel);
248     assert(channel->poller == NULL);
249         
250     channel->poller = this;
251     channels.push_back(channel);
252 }
253
254 void
255 NetChannelPoller::removeChannel(NetChannel* channel)
256 {
257     assert(channel);
258     assert(channel->poller == this);
259     channel->poller = NULL;
260     
261     // portability: MSVC throws assertion failure when empty
262     if (channels.empty()) {
263         return;
264     }
265
266     ChannelList::iterator it = channels.begin();
267     for (; it != channels.end(); ++it) {
268         if (*it == channel) {
269             channels.erase(it);
270             return;
271         }
272     }
273 }
274
275 bool
276 NetChannelPoller::poll(unsigned int timeout)
277 {
278     if (channels.empty()) {
279         return false;
280     }
281     
282     enum { MAX_SOCKETS = 256 } ;
283     Socket* reads [ MAX_SOCKETS+1 ] ;
284     Socket* writes [ MAX_SOCKETS+1 ] ;
285     int nreads = 0 ;
286     int nwrites = 0 ;
287     int nopen = 0 ;
288     
289     ChannelList::iterator it = channels.begin();
290     while( it != channels.end() )
291     {
292         NetChannel* ch = *it;
293         if ( ch -> should_delete )
294         {
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
297             ch->poller = NULL;
298             delete ch;
299             it = channels.erase(it);
300             continue;
301         }
302
303         ++it; // we've copied the pointer into ch
304         if ( ch->closed ) { 
305             continue;
306         }
307
308         if (ch -> resolving_host )
309         {
310             ch -> handleResolve();
311             continue;
312         }
313       
314         nopen++ ;
315         if (ch -> readable()) {
316           assert(nreads<MAX_SOCKETS);
317           reads[nreads++] = ch ;
318         }
319         if (ch -> writable()) {
320           assert(nwrites<MAX_SOCKETS);
321           writes[nwrites++] = ch ;
322         }
323     } // of array-filling pass
324     
325     reads[nreads] = NULL ;
326     writes[nwrites] = NULL ;
327
328     if (!nopen)
329       return false ;
330     if (!nreads && !nwrites)
331       return true ; //hmmm- should we shutdown?
332
333     Socket::select (reads, writes, timeout) ;
334
335     for ( int i=0; reads[i]; i++ )
336     {
337       NetChannel* ch = (NetChannel*)reads[i];
338       if ( ! ch -> closed )
339         ch -> handleReadEvent();
340     }
341
342     for ( int i=0; writes[i]; i++ )
343     {
344       NetChannel* ch = (NetChannel*)writes[i];
345       if ( ! ch -> closed )
346         ch -> handleWriteEvent();
347     }
348
349     return true ;
350 }
351
352 void
353 NetChannelPoller::loop (unsigned int timeout)
354 {
355   while ( poll (timeout) ) ;
356 }
357
358
359 } // of namespace simgear