]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
Display a warning message for Irix users.
[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)
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)
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 )
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
136     // Connect this socket to the host and the port specified on the
137     // command line
138 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
139     bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
140 #else
141     bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
142 #endif
143     name.sin_port = htons(port);
144
145     if ( connect(sock, (struct sockaddr *) &name, 
146                  sizeof(struct sockaddr_in)) != 0 )
147     {
148         closesocket(sock);
149         SG_LOG( SG_IO, SG_ALERT, 
150                 "Error: connect() failed in make_client_socket()" );
151         return INVALID_SOCKET;
152     }
153
154     return sock;
155 }
156
157
158 // Wrapper functions
159 size_t SGSocket::readsocket( int fd, void *buf, size_t count ) {
160 #if defined(_MSC_VER)
161     return ::recv( fd, (char *)buf, count, 0 );
162 #else
163     return ::read( fd, buf, count );
164 #endif
165 }
166
167 size_t SGSocket::writesocket( int fd, const void *buf, size_t count ) {
168 #if defined(_MSC_VER)
169     return ::send( fd, (const char*)buf, count, 0 );
170 #else
171     return ::write( fd, buf, count );
172 #endif
173 }
174
175 #if !defined(_MSC_VER)
176 int SGSocket::closesocket( int fd ) {
177     return ::close( fd );
178 }
179 #endif
180
181
182 // If specified as a server (in direction for now) open the master
183 // listening socket.  If specified as a client (out direction), open a
184 // connection to a server.
185 bool SGSocket::open( const SGProtocolDir d ) {
186     set_dir( d );
187
188     if ( port_str == "" || port_str == "any" ) {
189         port = 0; 
190     } else {
191         port = atoi( port_str.c_str() );
192     }
193     
194     // client_connections.clear();
195
196     if ( get_dir() == SG_IO_IN ) {
197         // this means server for now
198
199         // Setup socket to listen on.  Set "port" before making this
200         // call.  A port of "0" indicates that we want to let the os
201         // pick any available port.
202         sock = make_server_socket();
203         if ( sock == INVALID_SOCKET ) {
204             SG_LOG( SG_IO, SG_ALERT, "socket creation failed" );
205             return false;
206         }
207
208         SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
209
210         if ( sock_style == SOCK_DGRAM ) {
211             // Non-blocking UDP
212             nonblock();
213         } else {
214             // Blocking TCP
215             // Specify the maximum length of the connection queue
216             listen( sock, SG_MAX_SOCKET_QUEUE );
217         }
218
219     } else if ( get_dir() == SG_IO_OUT ) {
220         // this means client for now
221
222         sock = make_client_socket();
223         // TODO: check for error.
224
225         if ( sock_style == SOCK_DGRAM ) {
226             // Non-blocking UDP
227             nonblock();
228         }
229     } else if ( get_dir() == SG_IO_BI && sock_style == SOCK_STREAM ) {
230         // this means server for TCP sockets
231
232         // Setup socket to listen on.  Set "port" before making this
233         // call.  A port of "0" indicates that we want to let the os
234         // pick any available port.
235         sock = make_server_socket();
236         // TODO: check for error.
237
238         SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
239
240         // Blocking TCP
241         // Specify the maximum length of the connection queue
242         listen( sock, SG_MAX_SOCKET_QUEUE );
243     } else {
244         SG_LOG( SG_IO, SG_ALERT, 
245                 "Error:  bidirection mode not available for UDP sockets." );
246         return false;
247     }
248
249     if ( sock < 0 ) {
250         SG_LOG( SG_IO, SG_ALERT, "Error opening socket: " << hostname
251                 << ":" << port );
252         return false;
253     }
254
255     // extra SOCK_STREAM stuff
256     msgsock = INVALID_SOCKET;
257     first_read = false;
258
259     return true;
260 }
261
262
263 // read data from socket (server)
264 // read a block of data of specified size
265 int SGSocket::read( char *buf, int length ) {
266     if ( sock == INVALID_SOCKET ) {
267         return 0;
268     }
269
270     int result = 0;
271     // check for potential input
272     fd_set ready;
273     FD_ZERO(&ready);
274     FD_SET(sock, &ready);
275     struct timeval tv;
276     tv.tv_sec = 0;
277     tv.tv_usec = 0;
278
279     // test for any input available on sock (returning immediately, even if
280     // nothing)
281     select(32, &ready, 0, 0, &tv);
282
283     if ( FD_ISSET(sock, &ready) ) {
284         // cout << "data ready" << endl;
285
286         if ( sock_style == SOCK_STREAM ) {
287             if ( msgsock == INVALID_SOCKET ) {
288                 msgsock = accept(sock, 0, 0);
289                 closesocket(sock);
290                 sock = msgsock;
291             } else {
292                 result = readsocket( sock, buf, length );
293             }
294         } else {
295             result = readsocket( sock, buf, length );
296         }
297
298         if ( result != length ) {
299             SG_LOG( SG_IO, SG_INFO, 
300                     "Warning: read() not enough bytes." );
301         }
302     }
303
304     return result;
305 }
306
307
308 // read a line of data, length is max size of input buffer
309 int SGSocket::readline( char *buf, int length ) {
310     if ( sock == INVALID_SOCKET ) {
311         return 0;
312     }
313
314     // cout << "sock = " << sock << endl;
315
316     // check for potential input
317     fd_set ready;
318     FD_ZERO(&ready);
319     FD_SET(sock, &ready);
320     struct timeval tv;
321     tv.tv_sec = 0;
322     tv.tv_usec = 0;
323
324     // test for any input read on sock (returning immediately, even if
325     // nothing)
326     int result = select(32, &ready, 0, 0, &tv);
327     // cout << "result = " << result << endl;
328
329     if ( FD_ISSET(sock, &ready) ) {
330         // cout << "fd change state\n";
331         // read a chunk, keep in the save buffer until we have the
332         // requested amount read
333
334         if ( sock_style == SOCK_STREAM ) {
335             // cout << "sock_stream\n";
336             if ( msgsock == INVALID_SOCKET ) {
337                 // cout << "msgsock == invalid\n";
338                 msgsock = accept(sock, 0, 0);
339                 closesocket(sock);
340                 sock = msgsock;
341             } else {
342                 // cout << "ready to read\n";
343                 char *buf_ptr = save_buf + save_len;
344                 result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE
345                                      - save_len );
346                 // cout << "read result = " << result << endl;
347
348                 if ( result > 0 ) {
349                     first_read = true;
350                 }
351
352                 save_len += result;
353
354                 // Try and detect that the remote end died.  This
355                 // could cause problems so if you see connections
356                 // dropping for unexplained reasons, LOOK HERE!
357                 if ( result == 0 && save_len == 0 && first_read == true ) {
358                     SG_LOG( SG_IO, SG_ALERT, 
359                             "Connection closed by foreign host." );
360                     closesocket(sock);
361                     open( get_dir() );
362                 }
363             }
364         } else {
365             char *buf_ptr = save_buf + save_len;
366             result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
367             save_len += result;
368         }
369
370         // cout << "current read = " << buf_ptr << endl;
371         // cout << "current save_buf = " << save_buf << endl;
372         // cout << "save_len = " << save_len << endl;
373     } else {
374         // cout << "no data ready\n";
375     }
376
377     // look for the end of line in save_buf
378     int i;
379     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
380     if ( save_buf[i] == '\n' ) {
381         result = i + 1;
382     } else {
383         // no end of line yet
384         // cout << "no eol found" << endl;
385         return 0;
386     }
387     // cout << "line length = " << result << endl;
388
389     // we found an end of line
390
391     // copy to external buffer
392     strncpy( buf, save_buf, result );
393     buf[result] = '\0';
394     // cout << "sg_socket line = " << buf << endl;
395     
396     // shift save buffer
397     for ( i = result; i < save_len; ++i ) {
398         save_buf[ i - result ] = save_buf[i];
399     }
400     save_len -= result;
401
402     return result;
403 }
404
405
406 // write data to socket (client)
407 int SGSocket::write( const char *buf, const int length ) {
408     if ( sock == INVALID_SOCKET ) {
409         return 0;
410     }
411
412     bool error_condition = false;
413
414     if ( writesocket(sock, buf, length) < 0 ) {
415         SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
416         error_condition = true;
417     }
418
419 #if 0 
420     // check for any new client connection requests
421     fd_set ready;
422     FD_ZERO(&ready);
423     FD_SET(sock, &ready);
424     struct timeval tv;
425     tv.tv_sec = 0;
426     tv.tv_usec = 0;
427
428     // test for any input on sock (returning immediately, even if
429     // nothing)
430     select(32, &ready, 0, 0, &tv);
431
432     // any new connections?
433     if ( FD_ISSET(sock, &ready) ) {
434         int msgsock = accept(sock, 0, 0);
435         if ( msgsock < 0 ) {
436             SG_LOG( SG_IO, SG_ALERT, 
437                     "Error: accept() failed in write()" );
438             return 0;
439         } else {
440             client_connections.push_back( msgsock );
441         }
442     }
443
444     SG_LOG( SG_IO, SG_INFO, "Client connections = " << 
445             client_connections.size() );
446     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
447         int msgsock = client_connections[i];
448
449         // read and junk any possible incoming messages.
450         // char junk[ SG_IO_MAX_MSG_SIZE ];
451         // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
452
453         // write the interesting data to the socket
454         if ( writesocket(msgsock, buf, length) == SOCKET_ERROR ) {
455             SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
456             error_condition = true;
457         } else {
458 #ifdef _POSIX_SYNCHRONIZED_IO
459             // fdatasync(msgsock);
460 #else
461             // fsync(msgsock);
462 #endif
463         }
464     }
465 #endif
466
467     if ( error_condition ) {
468         return 0;
469     }
470
471     return length;
472 }
473
474
475 // write null terminated string to socket (server)
476 int SGSocket::writestring( const char *str ) {
477     if ( sock == INVALID_SOCKET ) {
478         return 0;
479     }
480
481     int length = strlen( str );
482     return write( str, length );
483 }
484
485
486 // close the port
487 bool SGSocket::close() {
488     if ( sock == INVALID_SOCKET ) {
489         return 0;
490     }
491
492     closesocket( sock );
493     return true;
494 }
495
496
497 // configure the socket as non-blocking
498 bool SGSocket::nonblock() {
499     if ( sock == INVALID_SOCKET ) {
500         return 0;
501     }
502
503 #if defined(_MSC_VER)
504     u_long arg = 1;
505     if (ioctlsocket( sock, FIONBIO, &arg ) != 0) {
506         int error_code = WSAGetLastError();
507         SG_LOG( SG_IO, SG_ALERT, 
508                 "Error " << error_code << ": unable to set non-blocking mode"
509 );
510             return false;
511     }
512 #else
513     fcntl( sock, F_SETFL, O_NONBLOCK );
514 #endif
515     return true;
516 }
517
518 #if defined(_MSC_VER)
519
520 bool SGSocket::wsock_init = false;
521
522 bool
523 SGSocket::wsastartup() {
524     WORD wVersionRequested;
525     WSADATA wsaData;
526
527     //wVersionRequested = MAKEWORD( 2, 2 );
528     wVersionRequested = MAKEWORD( 1, 1 );
529     int err = WSAStartup( wVersionRequested, &wsaData );
530     if (err != 0)
531     {
532         SG_LOG( SG_IO, SG_ALERT, "Error: Couldn't load winsock" );
533         return false;
534     }
535
536 #if 0
537     if ( LOBYTE( wsaData.wVersion ) != 2 ||
538         HIBYTE( wsaData.wVersion ) != 2 ) {
539         SG_LOG( SG_IO, SG_ALERT, "Couldn't load a suitable winsock");
540         WSACleanup( );
541         return false;
542     }
543 #endif
544     wsock_init = true;
545     return true;
546 }
547 #endif