JSBSim Flight Dynamics Model  1.0 (02 March 2017)
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 Software
13  Foundation; either version 2 of the License, or (at your option) any later
14  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 with
22  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23  Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25  Further information about the GNU Lesser General Public License can also be found on
26  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 #else
44 #include <fcntl.h>
45 #endif
46 #include <iostream>
47 #include <iomanip>
48 #include <cstring>
49 #include <cstdio>
50 #include "FGfdmSocket.h"
51 #include "string_utilities.h"
52 
53 using std::cout;
54 using std::cerr;
55 using std::endl;
56 using std::string;
57 
58 namespace JSBSim {
59 
60 IDENT(IdSrc,"$Id: FGfdmSocket.cpp,v 1.31 2015/03/22 12:19:31 bcoconni Exp $");
61 IDENT(IdHdr,ID_FDMSOCKET);
62 
63 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 CLASS IMPLEMENTATION
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
66 
67 #if defined(_MSC_VER) || defined(__MINGW32__)
68 static bool LoadWinSockDLL(void)
69 {
70  WSADATA wsaData;
71  if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
72  cout << "Winsock DLL not initialized ..." << endl;
73  return false;
74  }
75 
76  cout << "Winsock DLL loaded ..." << endl;
77  return true;
78 }
79 #endif
80 
81 FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol)
82 {
83  sckt = sckt_in = 0;
84  Protocol = (ProtocolType)protocol;
85  connected = false;
86 
87  #if defined(_MSC_VER) || defined(__MINGW32__)
88  if (!LoadWinSockDLL()) return;
89  #endif
90 
91  if (!is_number(address)) {
92  if ((host = gethostbyname(address.c_str())) == NULL) {
93  cout << "Could not get host net address by name..." << endl;
94  }
95  } else {
96  unsigned int ip;
97  ip = inet_addr(address.c_str());
98  if ((host = gethostbyaddr((char*)&ip, address.size(), PF_INET)) == NULL) {
99  cout << "Could not get host net address by number..." << endl;
100  }
101  }
102 
103  if (host != NULL) {
104  if (protocol == ptUDP) { //use udp protocol
105  sckt = socket(AF_INET, SOCK_DGRAM, 0);
106  cout << "Creating UDP socket on port " << port << endl;
107  }
108  else { //use tcp protocol
109  sckt = socket(AF_INET, SOCK_STREAM, 0);
110  cout << "Creating TCP socket on port " << port << endl;
111  }
112 
113  if (sckt >= 0) { // successful
114  memset(&scktName, 0, sizeof(struct sockaddr_in));
115  scktName.sin_family = AF_INET;
116  scktName.sin_port = htons(port);
117  memcpy(&scktName.sin_addr, host->h_addr_list[0], host->h_length);
118  int len = sizeof(struct sockaddr_in);
119  if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
120  cout << "Successfully connected to socket for output ..." << endl;
121  connected = true;
122  } else { // unsuccessful
123  cout << "Could not connect to socket for output ..." << endl;
124  }
125  } else { // unsuccessful
126  cout << "Could not create socket for FDM output, error = " << errno << endl;
127  }
128 
129  }
130  Debug(0);
131 }
132 
133 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 // assumes UDP socket on localhost, for inbound datagrams
135 FGfdmSocket::FGfdmSocket(int port, int protocol, int direction) // assumes UDP
136 {
137  sckt = -1;
138  connected = false;
139  Protocol = (ProtocolType)protocol;
140  Direction = (DirectionType) direction;
141 
142 #if defined(_MSC_VER) || defined(__MINGW32__)
143  if (!LoadWinSockDLL()) return;
144 #endif
145 
146  if (Protocol == ptUDP) { //use udp protocol
147  sckt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
148 #if defined(_MSC_VER) || defined(__MINGW32__)
149  u_long NonBlock = 1; // True
150  ioctlsocket(sckt, FIONBIO, &NonBlock);
151 #else
152  fcntl(sckt, F_SETFL, O_NONBLOCK);
153 #endif
154  cout << "Creating UDP input socket on port " << port << endl;
155  }
156 
157  if (sckt != -1) {
158  memset(&scktName, 0, sizeof(struct sockaddr_in));
159  scktName.sin_family = AF_INET;
160  scktName.sin_port = htons(port);
161  scktName.sin_addr.s_addr = htonl(INADDR_ANY);
162  int len = sizeof(struct sockaddr_in);
163  if (bind(sckt, (struct sockaddr*)&scktName, len) != -1) {
164  cout << "Successfully bound to UDP input socket on port " << port << endl <<endl;
165  connected = true;
166  } else { // unsuccessful
167  cout << "Could not bind to UDP input socket, error = " << errno << endl;
168  }
169  } else { // unsuccessful
170  cout << "Could not create socket for UDP input, error = " << errno << endl;
171  }
172 
173 
174  Debug(0);
175 }
176 
177 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178 
179 FGfdmSocket::FGfdmSocket(const string& address, int port) // assumes TCP
180 {
181  sckt = sckt_in = 0;
182  connected = false;
183  Protocol = ptTCP;
184 
185  #if defined(_MSC_VER) || defined(__MINGW32__)
186  if (!LoadWinSockDLL()) return;
187  #endif
188 
189  cout << "... Socket Configuration Sanity Check ..." << endl;
190  cout << "Host name... " << address << ", Port... " << port << "." << endl;
191  cout << "Host name... (char) " << address.c_str() << "." << endl;
192 
193  if (!is_number(address)) {
194  if ((host = gethostbyname(address.c_str())) == NULL) {
195  cout << "Could not get host net address by name..." << endl;
196  }
197  } else {
198  if ((host = gethostbyaddr(address.c_str(), address.size(), PF_INET)) == NULL) {
199  cout << "Could not get host net address by number..." << endl;
200  }
201  }
202 
203  if (host != NULL) {
204  cout << "Got host net address..." << endl;
205  sckt = socket(AF_INET, SOCK_STREAM, 0);
206 
207  if (sckt >= 0) { // successful
208  memset(&scktName, 0, sizeof(struct sockaddr_in));
209  scktName.sin_family = AF_INET;
210  scktName.sin_port = htons(port);
211  memcpy(&scktName.sin_addr, host->h_addr_list[0], host->h_length);
212  int len = sizeof(struct sockaddr_in);
213  if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
214  cout << "Successfully connected to socket for output ..." << endl;
215  connected = true;
216  } else { // unsuccessful
217  cout << "Could not connect to socket for output ..." << endl;
218  }
219  } else { // unsuccessful
220  cout << "Could not create socket for FDM output, error = " << errno << endl;
221  }
222  }
223  Debug(0);
224 }
225 
226 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227 
228 FGfdmSocket::FGfdmSocket(int port) // assumes TCP
229 {
230  connected = false;
231  unsigned long NoBlock = true;
232  Protocol = ptTCP;
233 
234  #if defined(_MSC_VER) || defined(__MINGW32__)
235  if (!LoadWinSockDLL()) return;
236  #endif
237 
238  sckt = socket(AF_INET, SOCK_STREAM, 0);
239 
240  if (sckt >= 0) { // successful
241  memset(&scktName, 0, sizeof(struct sockaddr_in));
242  scktName.sin_family = AF_INET;
243  scktName.sin_port = htons(port);
244  int len = sizeof(struct sockaddr_in);
245  if (bind(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
246  cout << "Successfully bound to socket for input on port " << port << endl;
247  if (listen(sckt, 5) >= 0) { // successful listen()
248  #if defined(_MSC_VER) || defined(__MINGW32__)
249  ioctlsocket(sckt, FIONBIO, &NoBlock);
250  sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
251  #else
252  ioctl(sckt, FIONBIO, &NoBlock);
253  sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
254  #endif
255  } else {
256  cerr << "Could not listen ..." << endl;
257  }
258  connected = true;
259  } else { // unsuccessful
260  cerr << "Could not bind to socket for input ..." << endl;
261  }
262  } else { // unsuccessful
263  cerr << "Could not create socket for FDM input, error = " << errno << endl;
264  }
265 
266  Debug(0);
267 }
268 
269 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270 
271 FGfdmSocket::~FGfdmSocket()
272 {
273  if (sckt) shutdown(sckt,2);
274  if (sckt_in) shutdown(sckt_in,2);
275  Debug(1);
276 }
277 
278 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
279 
280 string FGfdmSocket::Receive(void)
281 {
282  char buf[1024];
283  int len = sizeof(struct sockaddr_in);
284  int num_chars=0;
285  unsigned long NoBlock = true;
286  string data; // todo: should allocate this with a standard size as a
287  // class attribute and pass as a reference?
288 
289  if (sckt_in <= 0 && Protocol == ptTCP) {
290  #if defined(_MSC_VER) || defined(__MINGW32__)
291  sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
292  #else
293  sckt_in = accept(sckt, (struct sockaddr*)&scktName, (socklen_t*)&len);
294  #endif
295  if (sckt_in > 0) {
296  #if defined(_MSC_VER) || defined(__MINGW32__)
297  ioctlsocket(sckt_in, FIONBIO,&NoBlock);
298  #else
299  ioctl(sckt_in, FIONBIO, &NoBlock);
300  #endif
301  send(sckt_in, "Connected to JSBSim server\nJSBSim> ", 35, 0);
302  }
303  }
304 
305  if (sckt_in > 0) {
306  while ((num_chars = recv(sckt_in, buf, sizeof buf, 0)) > 0) {
307  data.append(buf, num_chars);
308  }
309 
310 #if defined(_MSC_VER)
311  // when nothing received and the error isn't "would block"
312  // then assume that the client has closed the socket.
313  if (num_chars == 0) {
314  DWORD err = WSAGetLastError ();
315  if (err != WSAEWOULDBLOCK) {
316  printf ("Socket Closed. back to listening\n");
317  closesocket (sckt_in);
318  sckt_in = -1;
319  }
320  }
321 #endif
322  }
323 
324  // this is for FGUDPInputSocket
325  if (sckt >= 0 && Protocol == ptUDP) {
326  struct sockaddr addr;
327  socklen_t fromlen = sizeof addr;
328  num_chars = recvfrom(sckt, buf, sizeof buf, 0, (struct sockaddr*)&addr, &fromlen);
329  if (num_chars != -1) data.append(buf, num_chars);
330  }
331 
332  return data;
333 }
334 
335 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336 
337 int FGfdmSocket::Reply(const string& text)
338 {
339  int num_chars_sent=0;
340 
341  if (sckt_in >= 0) {
342  num_chars_sent = send(sckt_in, text.c_str(), text.size(), 0);
343  send(sckt_in, "JSBSim> ", 8, 0);
344  } else {
345  cerr << "Socket reply must be to a valid socket" << endl;
346  return -1;
347  }
348  return num_chars_sent;
349 }
350 
351 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
352 
353 void FGfdmSocket::Close(void)
354 {
355  close(sckt_in);
356 }
357 
358 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
359 
360 void FGfdmSocket::Clear(void)
361 {
362  buffer.str(string());
363 }
364 
365 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366 
367 void FGfdmSocket::Clear(const string& s)
368 {
369  Clear();
370  buffer << s << ' ';
371 }
372 
373 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374 
375 void FGfdmSocket::Append(const char* item)
376 {
377  if (buffer.tellp() > 0) buffer << ',';
378  buffer << item;
379 }
380 
381 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
382 
383 void FGfdmSocket::Append(double item)
384 {
385  if (buffer.tellp() > 0) buffer << ',';
386  buffer << std::setw(12) << std::setprecision(7) << item;
387 }
388 
389 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 
391 void FGfdmSocket::Append(long item)
392 {
393  if (buffer.tellp() > 0) buffer << ',';
394  buffer << std::setw(12) << item;
395 }
396 
397 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398 
399 void FGfdmSocket::Send(void)
400 {
401  buffer << '\n';
402  string str = buffer.str();
403  if ((send(sckt,str.c_str(),str.size(),0)) <= 0) {
404  perror("send");
405  }
406 }
407 
408 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
409 
410 void FGfdmSocket::Send(const char *data, int length)
411 {
412  if ((send(sckt,data,length,0)) <= 0) {
413  perror("send");
414  }
415 }
416 
417 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418 // The bitmasked value choices are as follows:
419 // unset: In this case (the default) JSBSim would only print
420 // out the normally expected messages, essentially echoing
421 // the config files as they are read. If the environment
422 // variable is not set, debug_lvl is set to 1 internally
423 // 0: This requests JSBSim not to output any messages
424 // whatsoever.
425 // 1: This value explicity requests the normal JSBSim
426 // startup messages
427 // 2: This value asks for a message to be printed out when
428 // a class is instantiated
429 // 4: When this value is set, a message is displayed when a
430 // FGModel object executes its Run() method
431 // 8: When this value is set, various runtime state variables
432 // are printed out periodically
433 // 16: When set various parameters are sanity checked and
434 // a message is printed out when they go out of bounds
435 
436 void FGfdmSocket::Debug(int from)
437 {
438  if (debug_lvl <= 0) return;
439 
440  if (debug_lvl & 1) { // Standard console startup message output
441  if (from == 0) { // Constructor
442  }
443  }
444  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
445  if (from == 0) cout << "Instantiated: FGfdmSocket" << endl;
446  if (from == 1) cout << "Destroyed: FGfdmSocket" << endl;
447  }
448  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
449  }
450  if (debug_lvl & 8 ) { // Runtime state variables
451  }
452  if (debug_lvl & 16) { // Sanity checking
453  }
454  if (debug_lvl & 64) {
455  if (from == 0) { // Constructor
456  cout << IdSrc << endl;
457  cout << IdHdr << endl;
458  }
459  }
460 }
461 }