]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
My old email address is no longer valid ... point to my web page.
[simgear.git] / simgear / io / sg_socket.cxx
1 // sg_socket.cxx -- Socket I/O routines
2 //
3 // Written by Curtis Olson, started November 1999.
4 // Modified by Bernie Bright <bbright@bigpond.net.au>, May 2002.
5 //
6 // Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 //
22 // $Id$
23
24
25 #include <simgear/compiler.h>
26
27 #if defined( sgi )
28 #include <strings.h>
29 #endif
30
31 #include STL_IOSTREAM
32
33 #include <simgear/debug/logstream.hxx>
34
35 #include "sg_socket.hxx"
36
37 bool SGSocket::init = false;
38
39 SGSocket::SGSocket( const string& host, const string& port_, 
40                     const string& style ) :
41     hostname(host),
42     port_str(port_),
43     save_len(0),
44     client(0),
45     is_tcp(false),
46     is_server(false),
47     first_read(false),
48     timeout(0)
49 {
50     if (!init)
51     {
52         netInit(NULL, NULL);    // plib-1.4.2 compatible
53         init = true;
54     }
55
56     if ( style == "tcp" )
57     {
58         is_tcp = true;
59     }
60     else if ( style != "udp" )
61     {
62         SG_LOG( SG_IO, SG_ALERT,
63                 "Error: SGSocket() unknown style = " << style );
64     }
65
66     set_type( sgSocketType );
67 }
68
69
70 SGSocket::~SGSocket()
71 {
72     this->close();
73 }
74
75
76 bool
77 SGSocket::make_server_socket()
78 {
79     if (!sock.open( is_tcp ))
80     {
81         SG_LOG( SG_IO, SG_ALERT, 
82                 "Error: socket() failed in make_server_socket()" );
83         return false;
84     }
85
86     if (sock.bind( "", port ) < 0)
87     {
88         SG_LOG( SG_IO, SG_ALERT,
89                 "Error: bind() failed in make_server_socket()" );
90         sock.close();
91         return false;
92     }
93
94     return true;
95 }
96
97
98 bool
99 SGSocket::make_client_socket()
100 {
101     if (!sock.open( is_tcp ))
102     {
103         SG_LOG( SG_IO, SG_ALERT, 
104                 "Error: socket() failed in make_client_socket()" );
105         return false;
106     }
107
108     if (sock.connect( hostname.c_str(), port ) < 0)
109     {
110         SG_LOG( SG_IO, SG_ALERT, 
111                 "Error: connect() failed in make_client_socket()" );
112         sock.close();
113         return false;
114     }
115
116     return true;
117 }
118
119 // If specified as a server (in direction for now) open the master
120 // listening socket.  If specified as a client (out direction), open a
121 // connection to a server.
122 bool
123 SGSocket::open( SGProtocolDir direction )
124 {
125     set_dir( direction );
126
127     is_server = is_tcp &&
128         (direction == SG_IO_IN || direction == SG_IO_BI);
129
130     if ( port_str == "" || port_str == "any" ) {
131         port = 0; 
132     } else {
133         port = atoi( port_str.c_str() );
134     }
135     
136     if (direction == SG_IO_IN)
137     {
138         // this means server for now
139
140         // Setup socket to listen on.  Set "port" before making this
141         // call.  A port of "0" indicates that we want to let the os
142         // pick any available port.
143         if (!make_server_socket())
144         {
145             SG_LOG( SG_IO, SG_ALERT, "SG_IO_IN socket creation failed" );
146             return false;
147         }
148
149         if ( !is_tcp )
150         {
151             // Non-blocking UDP
152             nonblock();
153         }
154         else
155         {
156             // Blocking TCP
157             // Specify the maximum length of the connection queue
158             sock.listen( SG_MAX_SOCKET_QUEUE );
159         }
160
161     }
162     else if (direction == SG_IO_OUT)
163     {
164         // this means client for now
165
166         if (!make_client_socket())
167         {
168             SG_LOG( SG_IO, SG_ALERT, "SG_IO_OUT socket creation failed" );
169             return false;
170         }
171
172         if ( !is_tcp )
173         {
174             // Non-blocking UDP
175             nonblock();
176         }
177     }
178     else if (direction == SG_IO_BI && is_tcp)
179     {
180         // this means server for TCP sockets
181
182         // Setup socket to listen on.  Set "port" before making this
183         // call.  A port of "0" indicates that we want to let the os
184         // pick any available port.
185         if (!make_server_socket())
186         {
187             SG_LOG( SG_IO, SG_ALERT, "SG_IO_BI socket creation failed" );
188             return false;
189         }
190         // Blocking TCP
191         // Specify the maximum length of the connection queue
192         sock.listen( SG_MAX_SOCKET_QUEUE );
193     }
194     else
195     {
196         SG_LOG( SG_IO, SG_ALERT, 
197                 "Error:  bidirection mode not available for UDP sockets." );
198         return false;
199     }
200
201     first_read = false;
202
203     return true;
204 }
205
206
207 // read data from socket (server)
208 // read a block of data of specified size
209 int
210 SGSocket::read( char *buf, int length )
211 {
212     if (sock.getHandle() == -1 &&
213         (client == 0 || client->getHandle() == -1))
214     {
215         return 0;
216     }
217
218     // test for any input available on sock (returning immediately, even if
219     // nothing)
220     int result = poll();
221
222     if (result > 0)
223     {
224        if (is_tcp && is_server)
225         {
226             result = client->recv( buf, length );
227         }
228         else
229         {
230             result = sock.recv( buf, length );
231         }
232
233         if ( result != length )
234         {
235             SG_LOG( SG_IO, SG_INFO, 
236                     "Warning: read() not enough bytes." );
237         }
238     }
239
240     return result;
241 }
242
243
244 // read a line of data, length is max size of input buffer
245 int
246 SGSocket::readline( char *buf, int length )
247 {
248     if (sock.getHandle() == -1 &&
249         (client == 0 || client->getHandle() == -1))
250     {
251         return 0;
252     }
253
254     // test for any input read on sock (returning immediately, even if
255     // nothing)
256     int result = this->poll();
257
258     if (result > 0)
259     {
260         // read a chunk, keep in the save buffer until we have the
261         // requested amount read
262
263        if (is_tcp && is_server)
264         {
265             char *buf_ptr = save_buf + save_len;
266             result = client->recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
267                 
268             if ( result > 0 )
269             {
270                 first_read = true;
271             }
272
273             save_len += result;
274
275             // Try and detect that the remote end died.  This
276             // could cause problems so if you see connections
277             // dropping for unexplained reasons, LOOK HERE!
278             if (result == 0 && save_len == 0 && first_read == true)
279             {
280                 SG_LOG( SG_IO, SG_ALERT, 
281                         "Connection closed by foreign host." );
282                 delete client;
283                 client = 0;
284             }
285         }
286         else
287         {
288             char *buf_ptr = save_buf + save_len;
289             result = sock.recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
290             save_len += result;
291         }
292     }
293
294     // look for the end of line in save_buf
295     int i;
296     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i )
297         ;
298     if ( save_buf[i] == '\n' ) {
299         result = i + 1;
300     } else {
301         // no end of line yet
302         return 0;
303     }
304
305     // we found an end of line
306
307     // copy to external buffer
308     strncpy( buf, save_buf, result );
309     buf[result] = '\0';
310
311     // shift save buffer
312     //memmove( save_buf+, save_buf+, ? );
313     for ( i = result; i < save_len; ++i ) {
314         save_buf[ i - result ] = save_buf[i];
315     }
316     save_len -= result;
317
318     return result;
319 }
320
321
322 // write data to socket (client)
323 int
324 SGSocket::write( const char *buf, const int length )
325 {
326     netSocket* s = client == 0 ? &sock : client;
327     if (s->getHandle() == -1)
328     {
329         return 0;
330     }
331
332     bool error_condition = false;
333
334     if ( s->send( buf, length ) < 0 )
335     {
336         SG_LOG( SG_IO, SG_WARN, "Error writing to socket: " << port );
337         error_condition = true;
338     }
339
340     if ( error_condition ) {
341         return 0;
342     }
343
344     return length;
345 }
346
347
348 // write null terminated string to socket (server)
349 int
350 SGSocket::writestring( const char *str )
351 {
352     int length = strlen( str );
353     return this->write( str, length );
354 }
355
356
357 // close the port
358 bool
359 SGSocket::close()
360 {
361     delete client;
362     client = 0;
363
364     sock.close();
365     return true;
366 }
367
368
369 // configure the socket as non-blocking
370 bool
371 SGSocket::nonblock()
372 {
373     if (sock.getHandle() == -1) {
374         return false;
375     }
376
377     sock.setBlocking( false );
378     return true;
379 }
380
381 int
382 SGSocket::poll()
383 {
384     netSocket* readers[2];
385
386     readers[0] = client != 0 ? client : &sock;
387     readers[1] = 0;
388
389     netSocket* writers[1];
390     writers[0] = 0;
391
392     int result = netSocket::select( readers, writers, timeout );
393
394     if (result > 0 && is_server && client == 0)
395     {
396         // Accept a new client connection
397         netAddress addr;
398         int new_fd = sock.accept( &addr );
399         SG_LOG( SG_IO, SG_INFO, "Accepted connection from "
400                 << addr.getHost() << ":" << addr.getPort() );
401         client = new netSocket();
402         client->setHandle( new_fd );
403         return 0;
404     }
405
406     return result;
407 }