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