]> git.mxchange.org Git - simgear.git/blob - simgear/io/sg_socket.cxx
Added a touch of error checking to the screen dump routine, i.e. don't
[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 #else
35 #  include <sys/timeb.h>        // select()
36 #  include <winsock2.h>         // socket(), bind(), listen(), accept(),
37                                 // struct sockaddr_in, gethostbyname()
38 #  include <windows.h>
39 #  include <io.h>
40 #endif
41
42 #if defined( sgi )
43 #include <strings.h>
44 #endif
45
46 #include STL_STRING
47
48 #include <simgear/debug/logstream.hxx>
49
50 #include "sg_socket.hxx"
51
52 FG_USING_STD(string);
53
54
55 SGSocket::SGSocket( const string& host, const string& port, 
56                     const string& style ) :
57     save_len(0)
58 {
59     hostname = host;
60     port_str = port;
61
62     if ( style == "udp" ) {
63         sock_style = SOCK_DGRAM;
64     } else if ( style == "tcp" ) {
65         sock_style = SOCK_STREAM;
66     } else {
67         sock_style = SOCK_DGRAM;
68         FG_LOG( FG_IO, FG_ALERT,
69                 "Error: SGSocket() unknown style = " << style );
70     }
71
72     set_type( sgSocketType );
73 }
74
75
76 SGSocket::~SGSocket() {
77 }
78
79
80 int SGSocket::make_server_socket () {
81     struct sockaddr_in name;
82
83 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER )
84     int length;
85 #else
86     socklen_t length;
87 #endif
88      
89     // Create the socket.
90     sock = socket (PF_INET, sock_style, 0);
91     if (sock < 0) {
92         FG_LOG( FG_IO, FG_ALERT, 
93                 "Error: socket() failed in make_server_socket()" );
94         return -1;
95     }
96      
97     // Give the socket a name.
98     name.sin_family = AF_INET;
99     name.sin_addr.s_addr = INADDR_ANY;
100     name.sin_port = htons(port); // set port to zero to let system pick
101     name.sin_addr.s_addr = htonl (INADDR_ANY);
102     if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
103         FG_LOG( FG_IO, FG_ALERT,
104                 "Error: bind() failed in make_server_socket()" );
105         return -1;
106     }
107      
108     // Find the assigned port number
109     length = sizeof(struct sockaddr_in);
110     if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
111         FG_LOG( FG_IO, FG_ALERT,
112                 "Error: getsockname() failed in make_server_socket()" );
113         return -1;
114     }
115     port = ntohs(name.sin_port);
116
117     return sock;
118 }
119
120
121 int SGSocket::make_client_socket () {
122     struct sockaddr_in name;
123     struct hostent *hp;
124      
125     FG_LOG( FG_IO, FG_INFO, "Make client socket()" );
126
127     // Create the socket.
128     sock = socket (PF_INET, sock_style, 0);
129     if (sock < 0) {
130         FG_LOG( FG_IO, FG_ALERT, 
131                 "Error: socket() failed in make_client_socket()" );
132         return -1;
133     }
134      
135     // specify address family
136     name.sin_family = AF_INET;
137
138     // get the hosts official name/info
139     hp = gethostbyname( hostname.c_str() );
140
141     // Connect this socket to the host and the port specified on the
142     // command line
143 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ )
144     bcopy(hp->h_addr, (char *)(&(name.sin_addr.s_addr)), hp->h_length);
145 #else
146     bcopy(hp->h_addr, &(name.sin_addr.s_addr), hp->h_length);
147 #endif
148     name.sin_port = htons(port);
149
150     if ( connect(sock, (struct sockaddr *) &name, 
151                  sizeof(struct sockaddr_in)) < 0 )
152     {
153 #ifdef _MSC_VER
154         _close(sock);
155 #else
156         std::close(sock);
157 #endif
158         FG_LOG( FG_IO, FG_ALERT, 
159                 "Error: connect() failed in make_client_socket()" );
160         return -1;
161     }
162
163     return sock;
164 }
165
166
167 // If specified as a server (in direction for now) open the master
168 // listening socket.  If specified as a client (out direction), open a
169 // connection to a server.
170 bool SGSocket::open( SGProtocolDir dir ) {
171     if ( port_str == "" || port_str == "any" ) {
172         port = 0; 
173     } else {
174         port = atoi( port_str.c_str() );
175     }
176     
177     // client_connections.clear();
178
179     if ( dir == SG_IO_IN ) {
180         // this means server for now
181
182         // Setup socket to listen on.  Set "port" before making this
183         // call.  A port of "0" indicates that we want to let the os
184         // pick any available port.
185         sock = make_server_socket();
186         FG_LOG( FG_IO, FG_INFO, "socket is connected to port = " << port );
187
188         if ( sock_style == SOCK_DGRAM ) {
189             // Non-blocking UDP
190             fcntl( sock, F_SETFL, O_NONBLOCK );
191         } else {
192             // Blocking TCP
193             // Specify the maximum length of the connection queue
194             listen( sock, SG_MAX_SOCKET_QUEUE );
195         }
196
197     } else if ( dir == SG_IO_OUT ) {
198         // this means client for now
199
200         sock = make_client_socket();
201
202         if ( sock_style == SOCK_DGRAM ) {
203             // Non-blocking UDP
204             fcntl( sock, F_SETFL, O_NONBLOCK );
205         }
206     } else {
207         FG_LOG( FG_IO, FG_ALERT, 
208                 "Error:  bidirection mode not available yet for sockets." );
209         return false;
210     }
211
212     if ( sock < 0 ) {
213         FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
214                 << ":" << port );
215         return false;
216     }
217
218     return true;
219 }
220
221
222 // read data from socket (server)
223 // read a block of data of specified size
224 int SGSocket::read( char *buf, int length ) {
225     int result = 0;
226
227     // check for potential input
228     fd_set ready;
229     FD_ZERO(&ready);
230     FD_SET(sock, &ready);
231     struct timeval tv;
232     tv.tv_sec = 0;
233     tv.tv_usec = 0;
234
235     // test for any input available on sock (returning immediately, even if
236     // nothing)
237     select(32, &ready, 0, 0, &tv);
238
239     if ( FD_ISSET(sock, &ready) ) {
240 #ifdef _MSC_VER
241         result = _read( sock, buf, length );
242 #else
243         result = std::read( sock, buf, length );
244 #endif
245         if ( result != length ) {
246             FG_LOG( FG_IO, FG_INFO, 
247                     "Warning: read() not enough bytes." );
248         }
249     }
250
251     return result;
252 }
253
254
255 // read a line of data, length is max size of input buffer
256 int SGSocket::readline( char *buf, int length ) {
257     int result = 0;
258
259     // check for potential input
260     fd_set ready;
261     FD_ZERO(&ready);
262     FD_SET(sock, &ready);
263     struct timeval tv;
264     tv.tv_sec = 0;
265     tv.tv_usec = 0;
266
267     // test for any input read on sock (returning immediately, even if
268     // nothing)
269     select(32, &ready, 0, 0, &tv);
270
271     if ( FD_ISSET(sock, &ready) ) {
272         // read a chunk, keep in the save buffer until we have the
273         // requested amount read
274
275         char *buf_ptr = save_buf + save_len;
276 #ifdef _MSC_VER
277         result = _read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
278 #else
279         result = std::read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
280 #endif
281         save_len += result;
282         // cout << "current read = " << buf_ptr << endl;
283         // cout << "current save_buf = " << save_buf << endl;
284         // cout << "save_len = " << save_len << endl;
285     }
286
287     // look for the end of line in save_buf
288     int i;
289     for ( i = 0; i < save_len && save_buf[i] != '\n'; ++i );
290     if ( save_buf[i] == '\n' ) {
291         result = i + 1;
292     } else {
293         // no end of line yet
294         // cout << "no eol found" << endl;
295         return 0;
296     }
297     // cout << "line length = " << result << endl;
298
299     // we found an end of line
300
301     // copy to external buffer
302     strncpy( buf, save_buf, result );
303     buf[result] = '\0';
304     // cout << "sg_socket line = " << buf << endl;
305     
306     // shift save buffer
307     for ( i = result; i < save_len; ++i ) {
308         save_buf[ i - result ] = save_buf[i];
309     }
310     save_len -= result;
311
312     return result;
313 }
314
315
316 // write data to socket (client)
317 int SGSocket::write( char *buf, int length ) {
318     bool error_condition = false;
319
320 #ifdef _MSC_VER
321     if ( _write(sock, buf, length) < 0 ) {
322 #else
323     if ( std::write(sock, buf, length) < 0 ) {
324 #endif
325         FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
326         error_condition = true;
327     }
328
329 #if 0 
330     // check for any new client connection requests
331     fd_set ready;
332     FD_ZERO(&ready);
333     FD_SET(sock, &ready);
334     struct timeval tv;
335     tv.tv_sec = 0;
336     tv.tv_usec = 0;
337
338     // test for any input on sock (returning immediately, even if
339     // nothing)
340     select(32, &ready, 0, 0, &tv);
341
342     // any new connections?
343     if ( FD_ISSET(sock, &ready) ) {
344         int msgsock = accept(sock, 0, 0);
345         if ( msgsock < 0 ) {
346             FG_LOG( FG_IO, FG_ALERT, 
347                     "Error: accept() failed in write()" );
348             return 0;
349         } else {
350             client_connections.push_back( msgsock );
351         }
352     }
353
354     FG_LOG( FG_IO, FG_INFO, "Client connections = " << 
355             client_connections.size() );
356     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
357         int msgsock = client_connections[i];
358
359         // read and junk any possible incoming messages.
360         // char junk[ SG_IO_MAX_MSG_SIZE ];
361         // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
362
363         // write the interesting data to the socket
364 #ifdef _MSC_VER
365         if ( _write(msgsock, buf, length) < 0 ) {
366 #else
367         if ( std::write(msgsock, buf, length) < 0 ) {
368 #endif
369             FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
370             error_condition = true;
371         } else {
372 #ifdef _POSIX_SYNCHRONIZED_IO
373             // fdatasync(msgsock);
374 #else
375             // fsync(msgsock);
376 #endif
377         }
378     }
379 #endif
380
381     if ( error_condition ) {
382         return 0;
383     }
384
385     return length;
386 }
387
388
389 // write null terminated string to socket (server)
390 int SGSocket::writestring( char *str ) {
391     int length = strlen( str );
392     return write( str, length );
393 }
394
395
396 // close the port
397 bool SGSocket::close() {
398 #if 0
399     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
400         int msgsock = client_connections[i];
401 #ifdef _MSC_VER
402         _close( msgsock );
403 #else
404         std::close( msgsock );
405 #endif
406     }
407 #endif
408
409 #ifdef _MSC_VER
410     _close( sock );
411 #else
412     std::close( sock );
413 #endif
414     return true;
415 }