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