]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
Make sure both client and server situations are hendled properly
[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(NULL, NULL);    // plib-1.4.2 compatible
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        if (is_tcp && is_server)
222         {
223             result = client->recv( buf, length );
224         }
225         else
226         {
227             result = sock.recv( buf, length );
228         }
229
230         if ( result != length )
231         {
232             SG_LOG( SG_IO, SG_INFO, 
233                     "Warning: read() not enough bytes." );
234         }
235     }
236
237     return result;
238 }
239
240
241 // read a line of data, length is max size of input buffer
242 int
243 SGSocket::readline( char *buf, int length )
244 {
245     if (sock.getHandle() == -1 &&
246         (client == 0 || client->getHandle() == -1))
247     {
248         return 0;
249     }
250
251     // test for any input read on sock (returning immediately, even if
252     // nothing)
253     int result = this->poll();
254
255     if (result > 0)
256     {
257         // read a chunk, keep in the save buffer until we have the
258         // requested amount read
259
260        if (is_tcp && is_server)
261         {
262             char *buf_ptr = save_buf + save_len;
263             result = client->recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
264                 
265             if ( result > 0 )
266             {
267                 first_read = true;
268             }
269
270             save_len += result;
271
272             // Try and detect that the remote end died.  This
273             // could cause problems so if you see connections
274             // dropping for unexplained reasons, LOOK HERE!
275             if (result == 0 && save_len == 0 && first_read == true)
276             {
277                 SG_LOG( SG_IO, SG_ALERT, 
278                         "Connection closed by foreign host." );
279                 delete client;
280                 client = 0;
281             }
282         }
283         else
284         {
285             char *buf_ptr = save_buf + save_len;
286             result = sock.recv( buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
287             save_len += result;
288         }
289     }
290
291     // look for the end of line in save_buf
292     int i;
293     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i )
294         ;
295     if ( save_buf[i] == '\n' ) {
296         result = i + 1;
297     } else {
298         // no end of line yet
299         return 0;
300     }
301
302     // we found an end of line
303
304     // copy to external buffer
305     strncpy( buf, save_buf, result );
306     buf[result] = '\0';
307
308     // shift save buffer
309     //memmove( save_buf+, save_buf+, ? );
310     for ( i = result; i < save_len; ++i ) {
311         save_buf[ i - result ] = save_buf[i];
312     }
313     save_len -= result;
314
315     return result;
316 }
317
318
319 // write data to socket (client)
320 int
321 SGSocket::write( const char *buf, const int length )
322 {
323     netSocket* s = client == 0 ? &sock : client;
324     if (s->getHandle() == -1)
325     {
326         return 0;
327     }
328
329     bool error_condition = false;
330
331     if ( s->send( buf, length ) < 0 )
332     {
333         SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
334         error_condition = true;
335     }
336
337     if ( error_condition ) {
338         return 0;
339     }
340
341     return length;
342 }
343
344
345 // write null terminated string to socket (server)
346 int
347 SGSocket::writestring( const char *str )
348 {
349     int length = strlen( str );
350     return this->write( str, length );
351 }
352
353
354 // close the port
355 bool
356 SGSocket::close()
357 {
358     delete client;
359     client = 0;
360
361     sock.close();
362     return true;
363 }
364
365
366 // configure the socket as non-blocking
367 bool
368 SGSocket::nonblock()
369 {
370     if (sock.getHandle() == -1) {
371         return false;
372     }
373
374     sock.setBlocking( false );
375     return true;
376 }
377
378 int
379 SGSocket::poll()
380 {
381     netSocket* readers[2];
382
383     readers[0] = client != 0 ? client : &sock;
384     readers[1] = 0;
385
386     netSocket* writers[1];
387     writers[0] = 0;
388
389     int result = netSocket::select( readers, writers, 0 );
390
391     if (result > 0 && is_server && client == 0)
392     {
393         // Accept a new client connection
394         netAddress addr;
395         int new_fd = sock.accept( &addr );
396         SG_LOG( SG_IO, SG_INFO, "Accepted connection from "
397                 << addr.getHost() << ":" << addr.getPort() );
398         client = new netSocket();
399         client->setHandle( new_fd );
400         return 0;
401     }
402
403     return result;
404 }