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