JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
FGfdmSocket.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGfdmSocket.cpp
4  Author: Jon S. Berndt
5  Date started: 11/08/99
6  Purpose: Encapsulates a socket
7  Called by: FGOutput, et. al.
8 
9  ------------- Copyright (C) 1999 Jon S. Berndt (jon@jsbsim.org) -------------
10 
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU Lesser General Public License as published by the Free
13  Software Foundation; either version 2 of the License, or (at your option) any
14  later version.
15 
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19  details.
20 
21  You should have received a copy of the GNU Lesser General Public License along
22  with this program; if not, write to the Free Software Foundation, Inc., 59
23  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25  Further information about the GNU Lesser General Public License can also be
26  found on the world wide web at http://www.gnu.org.
27 
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 This class excapsulates a socket for simple data writing
31 
32 HISTORY
33 --------------------------------------------------------------------------------
34 11/08/99 JSB Created
35 11/08/07 HDW Added Generic Socket Send
36 
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 INCLUDES
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40 
41 #if defined(_MSC_VER) || defined(__MINGW32__)
42 #include <ws2tcpip.h>
43 #elif defined(__OpenBSD__)
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netdb.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #else
50 #include <fcntl.h>
51 #include <unistd.h>
52 #endif
53 #include <iomanip>
54 #include <cstring>
55 #include "FGfdmSocket.h"
56 
57 using std::cout;
58 using std::cerr;
59 using std::endl;
60 using std::string;
61 
62 namespace JSBSim {
63 
64 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 CLASS IMPLEMENTATION
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
67 
68 #if defined(_MSC_VER) || defined(__MINGW32__)
69 static bool LoadWinSockDLL(int debug_lvl)
70 {
71  WSADATA wsaData;
72  if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
73  cerr << "Winsock DLL not initialized ..." << endl;
74  return false;
75  }
76 
77  if (debug_lvl > 0)
78  cout << "Winsock DLL loaded ..." << endl;
79 
80  return true;
81 }
82 #endif
83 
84 FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol, int precision)
85 {
86  sckt = sckt_in = 0;
87  Protocol = (ProtocolType)protocol;
88  connected = false;
89  struct addrinfo *addr = nullptr;
90  this->precision = precision;
91 
92  #if defined(_MSC_VER) || defined(__MINGW32__)
93  if (!LoadWinSockDLL(debug_lvl)) return;
94  #endif
95 
96  struct addrinfo hints;
97  memset(&hints, 0, sizeof(struct addrinfo));
98  hints.ai_family = AF_INET;
99  if (protocol == ptUDP)
100  hints.ai_socktype = SOCK_DGRAM;
101  else
102  hints.ai_socktype = SOCK_STREAM;
103  hints.ai_protocol = 0;
104  if (!is_number(address))
105  hints.ai_flags = AI_ADDRCONFIG;
106  else
107  hints.ai_flags = AI_NUMERICHOST;
108 
109  int failure = getaddrinfo(address.c_str(), NULL, &hints, &addr);
110  if (failure || !addr) {
111  cerr << "Could not get host net address " << address;
112 
113  if (hints.ai_flags == AI_NUMERICHOST)
114  cerr << " by number..." << endl;
115  else
116  cerr << " by name..." << endl;
117 
118  cerr << gai_strerror(failure) << endl;
119 
120  freeaddrinfo(addr);
121  return;
122  }
123 
124  sckt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
125 
126  if (debug_lvl > 0) {
127  if (protocol == ptUDP) //use udp protocol
128  cout << "Creating UDP socket on port " << port << endl;
129  else //use tcp protocol
130  cout << "Creating TCP socket on port " << port << endl;
131  }
132 
133  if (sckt >= 0) { // successful
134  int len = sizeof(struct sockaddr_in);
135  memcpy(&scktName, addr->ai_addr, len);
136  scktName.sin_port = htons(port);
137 
138  if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
139  if (debug_lvl > 0)
140  cout << "Successfully connected to socket for output ..." << endl;
141  connected = true;
142  } else // unsuccessful
143  cerr << "Could not connect to socket for output ..." << endl;
144  } else // unsuccessful
145  cerr << "Could not create socket for FDM output, error = " << errno << endl;
146 
147  freeaddrinfo(addr);
148 
149  Debug(0);
150 }
151 
152 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 // assumes TCP or UDP socket on localhost, for inbound datagrams
154 FGfdmSocket::FGfdmSocket(int port, int protocol, int precision)
155 {
156  sckt = -1;
157  connected = false;
158  Protocol = (ProtocolType)protocol;
159  string ProtocolName;
160  this->precision = precision;
161 
162 #if defined(_MSC_VER) || defined(__MINGW32__)
163  if (!LoadWinSockDLL(debug_lvl)) return;
164 #endif
165 
166  if (Protocol == ptUDP) { //use udp protocol
167  ProtocolName = "UDP";
168  sckt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
169 #if defined(_MSC_VER) || defined(__MINGW32__)
170  u_long NonBlock = 1; // True
171  ioctlsocket(sckt, FIONBIO, &NonBlock);
172 #else
173  fcntl(sckt, F_SETFL, O_NONBLOCK);
174 #endif
175  }
176  else {
177  ProtocolName = "TCP";
178  sckt = socket(AF_INET, SOCK_STREAM, 0);
179  }
180 
181  if (debug_lvl > 0)
182  cout << "Creating input " << ProtocolName << " socket on port " << port
183  << endl;
184 
185  if (sckt != -1) {
186  memset(&scktName, 0, sizeof(struct sockaddr_in));
187  scktName.sin_family = AF_INET;
188  scktName.sin_port = htons(port);
189 
190  if (Protocol == ptUDP)
191  scktName.sin_addr.s_addr = htonl(INADDR_ANY);
192 
193  int len = sizeof(struct sockaddr_in);
194  if (bind(sckt, (struct sockaddr*)&scktName, len) != -1) {
195  if (debug_lvl > 0)
196  cout << "Successfully bound to " << ProtocolName
197  << " input socket on port " << port << endl << endl;
198 
199  if (Protocol == ptTCP) {
200  if (listen(sckt, 5) >= 0) { // successful listen()
201 #if defined(_MSC_VER) || defined(__MINGW32__)
202  u_long NoBlock = 1;
203  ioctlsocket(sckt, FIONBIO, &NoBlock);
204  sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
205 #else
206  int flags = fcntl(sckt, F_GETFL, 0);
207  fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
208  sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
209 #endif
210  connected = true;
211  } else
212  cerr << "Could not listen ..." << endl;
213  } else
214  connected = true;
215  } else // unsuccessful
216  cerr << "Could not bind to " << ProtocolName << " input socket, error = "
217  << errno << endl;
218  } else // unsuccessful
219  cerr << "Could not create " << ProtocolName << " socket for input, error = "
220  << errno << endl;
221 
222  Debug(0);
223 }
224 
225 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226 
227 FGfdmSocket::~FGfdmSocket()
228 {
229  if (sckt) shutdown(sckt,2);
230  if (sckt_in) shutdown(sckt_in,2);
231  Debug(1);
232 }
233 
234 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
235 
236 string FGfdmSocket::Receive(void)
237 {
238  char buf[1024];
239  int len = sizeof(struct sockaddr_in);
240  int num_chars=0;
241  string data; // todo: should allocate this with a standard size as a
242  // class attribute and pass as a reference?
243 
244  if (sckt_in <= 0 && Protocol == ptTCP) {
245  #if defined(_MSC_VER) || defined(__MINGW32__)
246  sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
247  #else
248  sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
249  #endif
250  if (sckt_in > 0) {
251  #if defined(_MSC_VER) || defined(__MINGW32__)
252  u_long NoBlock = 1;
253  ioctlsocket(sckt_in, FIONBIO, &NoBlock);
254  #else
255  int flags = fcntl(sckt_in, F_GETFL, 0);
256  fcntl(sckt_in, F_SETFL, flags | O_NONBLOCK);
257  #endif
258  send(sckt_in, "Connected to JSBSim server\nJSBSim> ", 35, 0);
259  }
260  }
261 
262  if (sckt_in > 0) {
263  while ((num_chars = recv(sckt_in, buf, sizeof buf, 0)) > 0) {
264  data.append(buf, num_chars);
265  }
266 
267 #if defined(_MSC_VER)
268  // when nothing received and the error isn't "would block"
269  // then assume that the client has closed the socket.
270  if (num_chars == 0) {
271  DWORD err = WSAGetLastError ();
272  if (err != WSAEWOULDBLOCK) {
273  printf ("Socket Closed. back to listening\n");
274  closesocket (sckt_in);
275  sckt_in = -1;
276  }
277  }
278 #endif
279  }
280 
281  // this is for FGUDPInputSocket
282  if (sckt >= 0 && Protocol == ptUDP) {
283  struct sockaddr addr;
284  socklen_t fromlen = sizeof addr;
285  num_chars = recvfrom(sckt, buf, sizeof buf, 0, (struct sockaddr*)&addr, &fromlen);
286  if (num_chars != -1) data.append(buf, num_chars);
287  }
288 
289  return data;
290 }
291 
292 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
293 
294 int FGfdmSocket::Reply(const string& text)
295 {
296  int num_chars_sent=0;
297 
298  if (sckt_in >= 0) {
299  num_chars_sent = send(sckt_in, text.c_str(), text.size(), 0);
300  send(sckt_in, "JSBSim> ", 8, 0);
301  } else {
302  cerr << "Socket reply must be to a valid socket" << endl;
303  return -1;
304  }
305  return num_chars_sent;
306 }
307 
308 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309 
310 void FGfdmSocket::Close(void)
311 {
312  close(sckt_in);
313 }
314 
315 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 
317 void FGfdmSocket::Clear(void)
318 {
319  buffer.str(string());
320 }
321 
322 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323 
324 void FGfdmSocket::Clear(const string& s)
325 {
326  Clear();
327  buffer << s << ' ';
328 }
329 
330 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331 
332 void FGfdmSocket::Append(const char* item)
333 {
334  if (buffer.tellp() > 0) buffer << ',';
335  buffer << item;
336 }
337 
338 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 
340 void FGfdmSocket::Append(double item)
341 {
342  if (buffer.tellp() > 0) buffer << ',';
343  buffer << std::setw(12) << std::setprecision(precision) << item;
344 }
345 
346 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 
348 void FGfdmSocket::Append(long item)
349 {
350  if (buffer.tellp() > 0) buffer << ',';
351  buffer << std::setw(12) << item;
352 }
353 
354 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
355 
356 void FGfdmSocket::Send(void)
357 {
358  buffer << '\n';
359  string str = buffer.str();
360  if ((send(sckt,str.c_str(),str.size(),0)) <= 0) {
361  perror("send");
362  }
363 }
364 
365 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366 
367 void FGfdmSocket::Send(const char *data, int length)
368 {
369  if ((send(sckt,data,length,0)) <= 0) {
370  perror("send");
371  }
372 }
373 
374 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 
376 void FGfdmSocket::WaitUntilReadable(void)
377 {
378  if (sckt_in <= 0)
379  return;
380 
381  fd_set fds;
382  FD_ZERO(&fds);
383  FD_SET(sckt_in, &fds);
384  select(sckt_in+1, &fds, NULL, NULL, NULL);
385 
386  /*
387  If you want to check select return status:
388 
389  int recVal = select(sckt_in+1, &fds, NULL, NULL, NULL);
390  recVal: received value
391  0, if socket timeout
392  -1, if socket error
393  anithing else, if socket is readable
394  */
395 }
396 
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 // The bitmasked value choices are as follows:
399 // unset: In this case (the default) JSBSim would only print
400 // out the normally expected messages, essentially echoing
401 // the config files as they are read. If the environment
402 // variable is not set, debug_lvl is set to 1 internally
403 // 0: This requests JSBSim not to output any messages
404 // whatsoever.
405 // 1: This value explicity requests the normal JSBSim
406 // startup messages
407 // 2: This value asks for a message to be printed out when
408 // a class is instantiated
409 // 4: When this value is set, a message is displayed when a
410 // FGModel object executes its Run() method
411 // 8: When this value is set, various runtime state variables
412 // are printed out periodically
413 // 16: When set various parameters are sanity checked and
414 // a message is printed out when they go out of bounds
415 
416 void FGfdmSocket::Debug(int from)
417 {
418  if (debug_lvl <= 0) return;
419 
420  if (debug_lvl & 1) { // Standard console startup message output
421  if (from == 0) { // Constructor
422  }
423  }
424  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
425  if (from == 0) cout << "Instantiated: FGfdmSocket" << endl;
426  if (from == 1) cout << "Destroyed: FGfdmSocket" << endl;
427  }
428  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
429  }
430  if (debug_lvl & 8 ) { // Runtime state variables
431  }
432  if (debug_lvl & 16) { // Sanity checking
433  }
434  if (debug_lvl & 64) {
435  if (from == 0) { // Constructor
436  }
437  }
438 }
439 }