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