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