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