JSBSim Flight Dynamics Model 1.3.3 (16 Oct 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
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
28FUNCTIONAL DESCRIPTION
29--------------------------------------------------------------------------------
30This class excapsulates a socket for simple data writing
31
32HISTORY
33--------------------------------------------------------------------------------
3411/08/99 JSB Created
3511/08/07 HDW Added Generic Socket Send
36
37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38INCLUDES
39%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
40
41#if defined(_MSC_VER) || defined(__MINGW32__)
42#include <ws2tcpip.h>
43#elif defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <netinet/in.h>
47#include <netdb.h>
48#include <sys/select.h>
49#include <sys/time.h>
50#include <unistd.h>
51#include <fcntl.h>
52#else
53#include <fcntl.h>
54#include <unistd.h>
55#endif
56#include <iomanip>
57#include <iostream>
58#include <sstream>
59#include <cstring>
60#include <assert.h>
61
62#include "FGfdmSocket.h"
63#include "input_output/string_utilities.h"
64#include "input_output/FGLog.h"
65
66using std::string;
67
68// Defines that make BSD/Unix sockets and Windows sockets syntax look alike.
69#ifndef _WIN32
70#define closesocket close
71#define INVALID_SOCKET -1
72#define SOCKET_ERROR -1
73#define SD_BOTH SHUT_RDWR
74#endif
75
76namespace JSBSim {
77
78/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79CLASS IMPLEMENTATION
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
81
82#ifdef _WIN32
83static bool LoadWinSockDLL(int debug_lvl)
84{
85 WSADATA wsaData;
86 if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
87 FGLogging log(LogLevel::ERROR);
88 log << "Winsock DLL not initialized ...\n";
89 return false;
90 }
91
92 if (debug_lvl > 0) {
93 FGLogging log(LogLevel::DEBUG);
94 log << "Winsock DLL loaded ...\n";
95 }
96
97 return true;
98}
99#endif
100
101FGfdmSocket::FGfdmSocket(const string& address, int port, int protocol, int precision)
102{
103 sckt = sckt_in = INVALID_SOCKET;
104 Protocol = (ProtocolType)protocol;
105 connected = false;
106 struct addrinfo *addr = nullptr;
107 this->precision = precision;
108
109#ifdef _WIN32
110 if (!LoadWinSockDLL(debug_lvl)) return;
111#endif
112
113 struct addrinfo hints;
114 memset(&hints, 0, sizeof(struct addrinfo));
115 hints.ai_family = AF_INET;
116 if (protocol == ptUDP)
117 hints.ai_socktype = SOCK_DGRAM;
118 else
119 hints.ai_socktype = SOCK_STREAM;
120 hints.ai_protocol = 0;
121 if (!is_number(address))
122 hints.ai_flags = AI_ADDRCONFIG;
123 else
124 hints.ai_flags = AI_NUMERICHOST;
125
126 int failure = getaddrinfo(address.c_str(), NULL, &hints, &addr);
127 if (failure || !addr) {
128 FGLogging log(LogLevel::ERROR);
129 log << "Could not get host net address " << address;
130
131 if (hints.ai_flags == AI_NUMERICHOST)
132 log << " by number...\n";
133 else
134 log << " by name...\n";
135
136 log << gai_strerror(failure) << "\n";
137
138 freeaddrinfo(addr);
139 return;
140 }
141
142 sckt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
143
144 if (debug_lvl > 0) {
145 FGLogging log(LogLevel::DEBUG);
146 if (protocol == ptUDP) //use udp protocol
147 log << "Creating UDP socket on port " << port << "\n";
148 else //use tcp protocol
149 log << "Creating TCP socket on port " << port << "\n";
150 }
151
152 if (sckt != INVALID_SOCKET) { // successful
153 int len = sizeof(struct sockaddr_in);
154 memcpy(&scktName, addr->ai_addr, len);
155 scktName.sin_port = htons(port);
156
157 if (connect(sckt, (struct sockaddr*)&scktName, len) == 0) { // successful
158 if (debug_lvl > 0) {
159 FGLogging log(LogLevel::DEBUG);
160 log << "Successfully connected to socket for output ...\n";
161 }
162 connected = true;
163 } else { // unsuccessful
164 FGLogging log(LogLevel::ERROR);
165 log << "Could not connect to socket for output ...\n";
166 }
167 } else { // unsuccessful
168 FGLogging log(LogLevel::ERROR);
169 log << "Could not create socket for FDM output, error = " << errno << "\n";
170 }
171
172 freeaddrinfo(addr);
173
174 Debug(0);
175}
176
177//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178// assumes TCP or UDP socket on localhost, for inbound datagrams
179FGfdmSocket::FGfdmSocket(int port, int protocol, int precision)
180{
181 sckt = INVALID_SOCKET;
182 connected = false;
183 Protocol = (ProtocolType)protocol;
184 string ProtocolName;
185 this->precision = precision;
186
187#ifdef _WIN32
188 if (!LoadWinSockDLL(debug_lvl)) return;
189#endif
190
191 if (Protocol == ptUDP) { //use udp protocol
192 ProtocolName = "UDP";
193 sckt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
194#ifdef _WIN32
195 u_long NonBlock = 1; // True
196 ioctlsocket(sckt, FIONBIO, &NonBlock);
197#else
198 int flags = fcntl(sckt, F_GETFL, 0);
199 fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
200#endif
201 }
202 else {
203 ProtocolName = "TCP";
204 sckt = socket(AF_INET, SOCK_STREAM, 0);
205 }
206
207 if (debug_lvl > 0) {
208 FGLogging log(LogLevel::DEBUG);
209 log << "Creating input " << ProtocolName << " socket on port " << port
210 << "\n";
211 }
212
213 if (sckt != INVALID_SOCKET) {
214 memset(&scktName, 0, sizeof(struct sockaddr_in));
215 scktName.sin_family = AF_INET;
216 scktName.sin_port = htons(port);
217
218 if (Protocol == ptUDP)
219 scktName.sin_addr.s_addr = htonl(INADDR_ANY);
220
221 socklen_t len = sizeof(struct sockaddr_in);
222 if (bind(sckt, (struct sockaddr*)&scktName, len) != SOCKET_ERROR) {
223 if (debug_lvl > 0) {
224 FGLogging log(LogLevel::DEBUG);
225 log << "Successfully bound to " << ProtocolName
226 << " input socket on port " << port << "\n\n";
227 }
228
229 if (Protocol == ptTCP) {
230 if (listen(sckt, 5) != SOCKET_ERROR) { // successful listen()
231 connected = true;
232#ifdef _WIN32
233 u_long NoBlock = 1;
234 ioctlsocket(sckt, FIONBIO, &NoBlock);
235#else
236 int flags = fcntl(sckt, F_GETFL, 0);
237 fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
238#endif
239 sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
240 } else {
241 closesocket(sckt);
242 sckt = INVALID_SOCKET;
243 FGLogging log(LogLevel::ERROR);
244 log << "Could not listen ...\n";
245 }
246 } else
247 connected = true;
248 } else { // unsuccessful
249 closesocket(sckt);
250 sckt = INVALID_SOCKET;
251 FGLogging log(LogLevel::ERROR);
252 log << "Could not bind to " << ProtocolName << " input socket, error = "
253 << errno << "\n";
254 }
255 } else { // unsuccessful
256 FGLogging log(LogLevel::ERROR);
257 log << "Could not create " << ProtocolName << " socket for input, error = "
258 << errno << "\n";
259 }
260
261 Debug(0);
262}
263
264//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265
266FGfdmSocket::~FGfdmSocket()
267{
268 // Release the file descriptors to the OS.
269 if (sckt_in != INVALID_SOCKET) shutdown(sckt_in, SD_BOTH);
270 if (sckt != INVALID_SOCKET) closesocket(sckt);
271 Debug(1);
272}
273
274//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275
277{
278 char buf[1024];
279 string data; // TODO should allocate this with a standard size as a
280 // class attribute and pass as a reference?
281
282 if (Protocol == ptTCP){
283 if (sckt_in == INVALID_SOCKET) {
284 socklen_t len = sizeof(struct sockaddr_in);
285 sckt_in = accept(sckt, (struct sockaddr*)&scktName, &len);
286 if (sckt_in != INVALID_SOCKET) {
287#ifdef _WIN32
288 u_long NoBlock = 1;
289 ioctlsocket(sckt_in, FIONBIO, &NoBlock);
290#else
291 int flags = fcntl(sckt_in, F_GETFL, 0);
292 fcntl(sckt_in, F_SETFL, flags | O_NONBLOCK);
293#endif
294 if (send(sckt_in, "Connected to JSBSim server\r\nJSBSim> ", 36, 0) == SOCKET_ERROR)
295 LogSocketError("Receive - TCP connection acknowledgement");
296 }
297 }
298
299 if (sckt_in != INVALID_SOCKET) {
300 int num_chars;
301
302 while ((num_chars = recv(sckt_in, buf, sizeof buf, 0)) > 0)
303 data.append(buf, num_chars);
304
305 if (num_chars == SOCKET_ERROR || num_chars == 0) {
306#ifdef _WIN32
307 if (WSAGetLastError() != WSAEWOULDBLOCK)
308#else
309 if (errno != EWOULDBLOCK)
310#endif
311 {
312 LogSocketError("Receive - TCP data reception");
313 // when nothing received and the error isn't "would block"
314 // then assume that the client has closed the socket.
315 FGLogging log(LogLevel::INFO);
316 log << "Socket Closed. Back to listening\n";
317 closesocket(sckt_in);
318 sckt_in = INVALID_SOCKET;
319 }
320 }
321 }
322 }
323
324 // this is for FGUDPInputSocket
325 if (sckt != INVALID_SOCKET && Protocol == ptUDP) {
326 struct sockaddr addr;
327 socklen_t fromlen = sizeof addr;
328 int num_chars = recvfrom(sckt, buf, sizeof buf, 0, (struct sockaddr*)&addr, &fromlen);
329 if (num_chars > 0) data.append(buf, num_chars);
330 if (num_chars == SOCKET_ERROR) {
331#ifdef _WIN32
332 if (WSAGetLastError() != WSAEWOULDBLOCK)
333#else
334 if (errno != EWOULDBLOCK)
335#endif
336 LogSocketError("Receive - UDP data reception");
337 }
338 }
339
340 return data;
341}
342
343//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344
345int FGfdmSocket::Reply(const string& text)
346{
347 int num_chars_sent=0;
348 assert(Protocol == ptTCP);
349
350 if (sckt_in != INVALID_SOCKET) {
351 num_chars_sent = send(sckt_in, text.c_str(), text.size(), 0);
352 if (num_chars_sent == SOCKET_ERROR) LogSocketError("Reply - Send data");
353 if (send(sckt_in, "JSBSim> ", 8, 0) == SOCKET_ERROR) LogSocketError("Reply - Prompt");
354 } else {
355 FGLogging log(LogLevel::ERROR);
356 log << "Socket reply must be to a valid socket\n";
357 return -1;
358 }
359 return num_chars_sent;
360}
361
362//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363
365{
366 assert(Protocol == ptTCP);
367 closesocket(sckt_in);
368 sckt_in = INVALID_SOCKET;
369}
370
371//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372
374{
375 buffer.str("");
376}
377
378//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379
380void FGfdmSocket::Clear(const string& s)
381{
382 Clear();
383 buffer << s << ' ';
384}
385
386//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
387
388void FGfdmSocket::Append(const char* item)
389{
390 if (buffer.tellp() > 0) buffer << ',';
391 buffer << item;
392}
393
394//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395
396void FGfdmSocket::Append(double item)
397{
398 if (buffer.tellp() > 0) buffer << ',';
399 buffer << std::setw(12) << std::setprecision(precision) << item;
400}
401
402//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403
404void FGfdmSocket::Append(long item)
405{
406 if (buffer.tellp() > 0) buffer << ',';
407 buffer << std::setw(12) << item;
408}
409
410//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411
413{
414 buffer << '\n';
415 string str = buffer.str();
416 Send(str.c_str(), str.size());
417}
418
419//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420
421void FGfdmSocket::Send(const char *data, int length)
422{
423 if (Protocol == ptTCP && sckt_in != INVALID_SOCKET) {
424 if ((send(sckt_in, data, length, 0)) == SOCKET_ERROR) LogSocketError("Send - TCP data sending");
425 return;
426 }
427
428 if (Protocol == ptUDP && sckt != INVALID_SOCKET) {
429 if ((send(sckt, data, length, 0)) == SOCKET_ERROR) LogSocketError("Send - UDP data sending");
430 return;
431 }
432
433 FGLogging log(LogLevel::ERROR);
434 log << "Data sending must be to a valid socket\n";
435}
436
437//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438
440{
441 assert(Protocol == ptTCP);
442 if (sckt_in == INVALID_SOCKET) return;
443
444 fd_set fds;
445 FD_ZERO(&fds);
446 FD_SET(sckt_in, &fds);
447
448 int result = select(FD_SETSIZE, &fds, nullptr, nullptr, nullptr);
449
450 if (result == 0) {
451 FGLogging log(LogLevel::ERROR);
452 log << "Socket timeout.\n";
453 return;
454 } else if (result != SOCKET_ERROR)
455 return;
456
457 LogSocketError("WaitUntilReadable");
458}
459
460//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461
462void FGfdmSocket::LogSocketError(const std::string& msg)
463{
464 // An error has occurred, display the error message.
465 FGLogging log(LogLevel::ERROR);
466 log << "Socket error in " << msg << ": ";
467#ifdef _WIN32
468 LPSTR errorMessage = nullptr;
469 DWORD errorCode = WSAGetLastError();
470 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
471 nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&errorMessage, 0, nullptr);
472 log << errorMessage << "\n";
473 LocalFree(errorMessage);
474#else
475 log << strerror(errno) << "\n";
476#endif
477}
478
479//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480// The bitmasked value choices are as follows:
481// unset: In this case (the default) JSBSim would only print
482// out the normally expected messages, essentially echoing
483// the config files as they are read. If the environment
484// variable is not set, debug_lvl is set to 1 internally
485// 0: This requests JSBSim not to output any messages
486// whatsoever.
487// 1: This value explicity requests the normal JSBSim
488// startup messages
489// 2: This value asks for a message to be printed out when
490// a class is instantiated
491// 4: When this value is set, a message is displayed when a
492// FGModel object executes its Run() method
493// 8: When this value is set, various runtime state variables
494// are printed out periodically
495// 16: When set various parameters are sanity checked and
496// a message is printed out when they go out of bounds
497
498void FGfdmSocket::Debug(int from)
499{
500 if (debug_lvl <= 0) return;
501
502 if (debug_lvl & 1) { // Standard console startup message output
503 if (from == 0) { // Constructor
504 }
505 }
506 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
507 FGLogging log(LogLevel::DEBUG);
508 if (from == 0) log << "Instantiated: FGfdmSocket\n";
509 if (from == 1) log << "Destroyed: FGfdmSocket\n";
510 }
511 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
512 }
513 if (debug_lvl & 8 ) { // Runtime state variables
514 }
515 if (debug_lvl & 16) { // Sanity checking
516 }
517 if (debug_lvl & 64) {
518 if (from == 0) { // Constructor
519 }
520 }
521}
522}
std::string Receive(void)
Receive data from the socket connection.
void Clear(void)
Clear the internal buffer.
void Append(const std::string &s)
Append the specified string to the internal buffer.
void Close(void)
Close the socket connection if the protocol is TCP.
void WaitUntilReadable(void)
Wait until the TCP socket is readable.
FGfdmSocket(const std::string &address, int port, int protocol, int precision=7)
Construct a client socket.
int Reply(const std::string &text)
Send a reply to the client ending by a prompt "JSBSim>".
void Send(void)
Send the internal buffer over the socket connection.
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:73