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