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