]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
Patches from Erik Hoffman:
[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 //
5 // Copyright (C) 1999  Curtis L. Olson - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <simgear/compiler.h>
25
26 #if !defined(_MSC_VER) && !defined(__MINGW32__)
27 #  include <sys/time.h>         // select()
28 #  include <sys/types.h>        // socket(), bind(), select(), accept()
29 #  include <sys/socket.h>       // socket(), bind(), listen(), accept()
30 #  include <netinet/in.h>       // struct sockaddr_in
31 #  include <netdb.h>            // gethostbyname()
32 #  include <unistd.h>           // select(), fsync()/fdatasync(), fcntl()
33 #  include <fcntl.h>            // fcntl()
34 #endif
35
36 #if defined( sgi )
37 #include <strings.h>
38 #endif
39
40 #include <simgear/debug/logstream.hxx>
41
42 #include "sg_socket.hxx"
43
44
45 SGSocket::SGSocket( const string& host, const string& port, 
46                     const string& style ) :
47     hostname(host),
48     port_str(port),
49     save_len(0)
50 {
51 #if defined(_MSC_VER) || defined(__MINGW32__)
52     if (!wsock_init && !wsastartup()) {
53         SG_LOG( SG_IO, SG_ALERT, "Winsock not available");
54     }
55 #endif
56
57     if ( style == "udp" ) {
58         sock_style = SOCK_DGRAM;
59     } else if ( style == "tcp" ) {
60         sock_style = SOCK_STREAM;
61     } else {
62         sock_style = SOCK_DGRAM;
63         SG_LOG( SG_IO, SG_ALERT,
64                 "Error: SGSocket() unknown style = " << style );
65     }
66
67     set_type( sgSocketType );
68 }
69
70
71 SGSocket::~SGSocket() {
72 }
73
74
75 SGSocket::SocketType SGSocket::make_server_socket () {
76     struct sockaddr_in name;
77
78 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER ) || defined(__MINGW32__) || defined( __APPLE__ )
79     int length;
80 #else
81     socklen_t length;
82 #endif
83      
84     // Create the socket.
85     sock = socket (PF_INET, sock_style, 0);
86     if (sock == INVALID_SOCKET) {
87         SG_LOG( SG_IO, SG_ALERT, 
88                 "Error: socket() failed in make_server_socket()" );
89         return INVALID_SOCKET;
90     }
91      
92     // Give the socket a name.
93     name.sin_family = AF_INET;
94     name.sin_addr.s_addr = INADDR_ANY;
95     name.sin_port = htons(port); // set port to zero to let system pick
96     name.sin_addr.s_addr = htonl (INADDR_ANY);
97     if (bind (sock, (struct sockaddr *) &name, sizeof (name)) != 0) {
98             SG_LOG( SG_IO, SG_ALERT,
99                     "Error: bind() failed in make_server_socket()" );
100         return INVALID_SOCKET;
101     }
102
103     // Find the assigned port number
104     length = sizeof(struct sockaddr_in);
105     if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
106         SG_LOG( SG_IO, SG_ALERT,
107                 "Error: getsockname() failed in make_server_socket()" );
108         return INVALID_SOCKET;
109     }
110     port = ntohs(name.sin_port);
111
112     return sock;
113 }
114
115
116 SGSocket::SocketType SGSocket::make_client_socket () {
117     struct sockaddr_in name;
118     struct hostent *hp;
119      
120     SG_LOG( SG_IO, SG_INFO, "Make client socket()" );
121
122     // Create the socket.
123     sock = socket (PF_INET, sock_style, 0);
124     if (sock == INVALID_SOCKET) {
125         SG_LOG( SG_IO, SG_ALERT, 
126                 "Error: socket() failed in make_server_socket()" );
127         return INVALID_SOCKET;
128     }
129      
130     // specify address family
131     name.sin_family = AF_INET;
132
133     // get the hosts official name/info
134     hp = gethostbyname( hostname.c_str() );
135     if (hp == NULL) {
136         SG_LOG( SG_IO, SG_ALERT, "Error: hostname lookup failed" );
137         return INVALID_SOCKET;
138     }
139
140     // Connect this socket to the host and the port specified on the
141     // command line
142 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
143     bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
144 #else
145     bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
146 #endif
147     name.sin_port = htons(port);
148
149     if ( connect(sock, (struct sockaddr *) &name, 
150                  sizeof(struct sockaddr_in)) != 0 )
151     {
152         closesocket(sock);
153         SG_LOG( SG_IO, SG_ALERT, 
154                 "Error: connect() failed in make_client_socket()" );
155         return INVALID_SOCKET;
156     }
157
158     return sock;
159 }
160
161
162 // Wrapper functions
163 int SGSocket::readsocket( int fd, void *buf, size_t count ) {
164 #if defined(_MSC_VER) || defined(__MINGW32__)
165     return ::recv( fd, (char *)buf, count, 0 );
166 #else
167     return ::read( fd, buf, count );
168 #endif
169 }
170
171 int SGSocket::writesocket( int fd, const void *buf, size_t count ) {
172 #if defined(_MSC_VER) || defined(__MINGW32__)
173     return ::send( fd, (const char*)buf, count, 0 );
174 #else
175     return ::write( fd, buf, count );
176 #endif
177 }
178
179 #if !defined(_MSC_VER) && !defined(__MINGW32__)
180 int SGSocket::closesocket( int fd ) {
181     return ::close( fd );
182 }
183 #endif
184
185
186 // If specified as a server (in direction for now) open the master
187 // listening socket.  If specified as a client (out direction), open a
188 // connection to a server.
189 bool SGSocket::open( const SGProtocolDir d ) {
190     set_dir( d );
191
192     if ( port_str == "" || port_str == "any" ) {
193         port = 0; 
194     } else {
195         port = atoi( port_str.c_str() );
196     }
197     
198     // client_connections.clear();
199
200     if ( get_dir() == SG_IO_IN ) {
201         // this means server for now
202
203         // Setup socket to listen on.  Set "port" before making this
204         // call.  A port of "0" indicates that we want to let the os
205         // pick any available port.
206         sock = make_server_socket();
207         if ( sock == INVALID_SOCKET ) {
208             SG_LOG( SG_IO, SG_ALERT, "socket creation failed" );
209             return false;
210         }
211
212         SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
213
214         if ( sock_style == SOCK_DGRAM ) {
215             // Non-blocking UDP
216             nonblock();
217         } else {
218             // Blocking TCP
219             // Specify the maximum length of the connection queue
220             listen( sock, SG_MAX_SOCKET_QUEUE );
221         }
222
223     } else if ( get_dir() == SG_IO_OUT ) {
224         // this means client for now
225
226         sock = make_client_socket();
227         // TODO: check for error.
228
229         if ( sock_style == SOCK_DGRAM ) {
230             // Non-blocking UDP
231             nonblock();
232         }
233     } else if ( get_dir() == SG_IO_BI && sock_style == SOCK_STREAM ) {
234         // this means server for TCP sockets
235
236         // Setup socket to listen on.  Set "port" before making this
237         // call.  A port of "0" indicates that we want to let the os
238         // pick any available port.
239         sock = make_server_socket();
240         // TODO: check for error.
241
242         SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
243
244         // Blocking TCP
245         // Specify the maximum length of the connection queue
246         listen( sock, SG_MAX_SOCKET_QUEUE );
247     } else {
248         SG_LOG( SG_IO, SG_ALERT, 
249                 "Error:  bidirection mode not available for UDP sockets." );
250         return false;
251     }
252
253     if ( sock < 0 ) {
254         SG_LOG( SG_IO, SG_ALERT, "Error opening socket: " << hostname
255                 << ":" << port );
256         return false;
257     }
258
259     // extra SOCK_STREAM stuff
260     msgsock = INVALID_SOCKET;
261     first_read = false;
262
263     return true;
264 }
265
266
267 // read data from socket (server)
268 // read a block of data of specified size
269 int SGSocket::read( char *buf, int length ) {
270     if ( sock == INVALID_SOCKET ) {
271         return 0;
272     }
273
274     int result = 0;
275     // check for potential input
276     fd_set ready;
277     FD_ZERO(&ready);
278     FD_SET(sock, &ready);
279     struct timeval tv;
280     tv.tv_sec = 0;
281     tv.tv_usec = 0;
282
283     // test for any input available on sock (returning immediately, even if
284     // nothing)
285     select(32, &ready, 0, 0, &tv);
286
287     if ( FD_ISSET(sock, &ready) ) {
288         // cout << "data ready" << endl;
289
290         if ( sock_style == SOCK_STREAM ) {
291             if ( msgsock == INVALID_SOCKET ) {
292                 msgsock = accept(sock, 0, 0);
293                 closesocket(sock);
294                 sock = msgsock;
295             } else {
296                 result = readsocket( sock, buf, length );
297             }
298         } else {
299             result = readsocket( sock, buf, length );
300         }
301
302         if ( result != length ) {
303             SG_LOG( SG_IO, SG_INFO, 
304                     "Warning: read() not enough bytes." );
305         }
306     }
307
308     return result;
309 }
310
311
312 // read a line of data, length is max size of input buffer
313 int SGSocket::readline( char *buf, int length ) {
314     if ( sock == INVALID_SOCKET ) {
315         return 0;
316     }
317
318     // cout << "sock = " << sock << endl;
319
320     // check for potential input
321     fd_set ready;
322     FD_ZERO(&ready);
323     FD_SET(sock, &ready);
324     struct timeval tv;
325     tv.tv_sec = 0;
326     tv.tv_usec = 0;
327
328     // test for any input read on sock (returning immediately, even if
329     // nothing)
330     int result = select(32, &ready, 0, 0, &tv);
331     // cout << "result = " << result << endl;
332
333     if ( FD_ISSET(sock, &ready) ) {
334         // cout << "fd change state\n";
335         // read a chunk, keep in the save buffer until we have the
336         // requested amount read
337
338         if ( sock_style == SOCK_STREAM ) {
339             // cout << "sock_stream\n";
340             if ( msgsock == INVALID_SOCKET ) {
341                 // cout << "msgsock == invalid\n";
342                 msgsock = sock;
343                 sock = accept(msgsock, 0, 0);
344             } else {
345                 // cout << "ready to read\n";
346                 char *buf_ptr = save_buf + save_len;
347                 result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE
348                                      - save_len );
349                 // cout << "read result = " << result << endl;
350
351                 if ( result > 0 ) {
352                     first_read = true;
353                 }
354
355                 save_len += result;
356
357                 // Try and detect that the remote end died.  This
358                 // could cause problems so if you see connections
359                 // dropping for unexplained reasons, LOOK HERE!
360                 if ( result == 0 && save_len == 0 && first_read == true ) {
361                     SG_LOG( SG_IO, SG_ALERT, 
362                             "Connection closed by foreign host." );
363                     close();
364                 }
365             }
366         } else {
367             char *buf_ptr = save_buf + save_len;
368             result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
369             save_len += result;
370         }
371
372         // cout << "current read = " << buf_ptr << endl;
373         // cout << "current save_buf = " << save_buf << endl;
374         // cout << "save_len = " << save_len << endl;
375     } else {
376         // cout << "no data ready\n";
377     }
378
379     // look for the end of line in save_buf
380     int i;
381     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
382     if ( save_buf[i] == '\n' ) {
383         result = i + 1;
384     } else {
385         // no end of line yet
386         // cout << "no eol found" << endl;
387         return 0;
388     }
389     // cout << "line length = " << result << endl;
390
391     // we found an end of line
392
393     // copy to external buffer
394     strncpy( buf, save_buf, result );
395     buf[result] = '\0';
396     // cout << "sg_socket line = " << buf << endl;
397     
398     // shift save buffer
399     for ( i = result; i < save_len; ++i ) {
400         save_buf[ i - result ] = save_buf[i];
401     }
402     save_len -= result;
403
404     return result;
405 }
406
407
408 // write data to socket (client)
409 int SGSocket::write( const char *buf, const int length ) {
410     if ( sock == INVALID_SOCKET ) {
411         return 0;
412     }
413
414     bool error_condition = false;
415
416     if ( writesocket(sock, buf, length) < 0 ) {
417         SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
418         error_condition = true;
419     }
420
421 #if 0 
422     // check for any new client connection requests
423     fd_set ready;
424     FD_ZERO(&ready);
425     FD_SET(sock, &ready);
426     struct timeval tv;
427     tv.tv_sec = 0;
428     tv.tv_usec = 0;
429
430     // test for any input on sock (returning immediately, even if
431     // nothing)
432     select(32, &ready, 0, 0, &tv);
433
434     // any new connections?
435     if ( FD_ISSET(sock, &ready) ) {
436         int msgsock = accept(sock, 0, 0);
437         if ( msgsock < 0 ) {
438             SG_LOG( SG_IO, SG_ALERT, 
439                     "Error: accept() failed in write()" );
440             return 0;
441         } else {
442             client_connections.push_back( msgsock );
443         }
444     }
445
446     SG_LOG( SG_IO, SG_INFO, "Client connections = " << 
447             client_connections.size() );
448     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
449         int msgsock = client_connections[i];
450
451         // read and junk any possible incoming messages.
452         // char junk[ SG_IO_MAX_MSG_SIZE ];
453         // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
454
455         // write the interesting data to the socket
456         if ( writesocket(msgsock, buf, length) == SOCKET_ERROR ) {
457             SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
458             error_condition = true;
459         } else {
460 #ifdef _POSIX_SYNCHRONIZED_IO
461             // fdatasync(msgsock);
462 #else
463             // fsync(msgsock);
464 #endif
465         }
466     }
467 #endif
468
469     if ( error_condition ) {
470         return 0;
471     }
472
473     return length;
474 }
475
476
477 // write null terminated string to socket (server)
478 int SGSocket::writestring( const char *str ) {
479     if ( sock == INVALID_SOCKET ) {
480         return 0;
481     }
482
483     int length = strlen( str );
484     return write( str, length );
485 }
486
487
488 // close the port
489 bool SGSocket::close() {
490     if ( sock == INVALID_SOCKET ) {
491         return 0;
492     }
493
494     closesocket( sock );
495     if ( sock_style == SOCK_STREAM && msgsock != INVALID_SOCKET ) {
496         sock = msgsock;
497         msgsock = INVALID_SOCKET;
498     }
499     return true;
500 }
501
502
503 // configure the socket as non-blocking
504 bool SGSocket::nonblock() {
505     if ( sock == INVALID_SOCKET ) {
506         return 0;
507     }
508
509 #if defined(_MSC_VER) || defined(__MINGW32__)
510     u_long arg = 1;
511     if (ioctlsocket( sock, FIONBIO, &arg ) != 0) {
512         int error_code = WSAGetLastError();
513         SG_LOG( SG_IO, SG_ALERT, 
514                 "Error " << error_code << ": unable to set non-blocking mode"
515 );
516             return false;
517     }
518 #else
519     fcntl( sock, F_SETFL, O_NONBLOCK );
520 #endif
521     return true;
522 }
523
524 #if defined(_MSC_VER) || defined(__MINGW32__)
525
526 bool SGSocket::wsock_init = false;
527
528 bool
529 SGSocket::wsastartup() {
530     WORD wVersionRequested;
531     WSADATA wsaData;
532
533     //wVersionRequested = MAKEWORD( 2, 2 );
534     wVersionRequested = MAKEWORD( 1, 1 );
535     int err = WSAStartup( wVersionRequested, &wsaData );
536     if (err != 0)
537     {
538         SG_LOG( SG_IO, SG_ALERT, "Error: Couldn't load winsock" );
539         return false;
540     }
541
542 #if 0
543     if ( LOBYTE( wsaData.wVersion ) != 2 ||
544         HIBYTE( wsaData.wVersion ) != 2 ) {
545         SG_LOG( SG_IO, SG_ALERT, "Couldn't load a suitable winsock");
546         WSACleanup( );
547         return false;
548     }
549 #endif
550     wsock_init = true;
551     return true;
552 }
553 #endif