41#if defined(_MSC_VER) || defined(__MINGW32__)
43#elif defined(__OpenBSD__) || defined(__EMSCRIPTEN__)
45#include <sys/socket.h>
46#include <netinet/in.h>
48#include <sys/select.h>
62#include "FGfdmSocket.h"
63#include "input_output/string_utilities.h"
64#include "input_output/FGLog.h"
70#define closesocket close
71#define INVALID_SOCKET -1
72#define SOCKET_ERROR -1
73#define SD_BOTH SHUT_RDWR
83static bool LoadWinSockDLL(
int debug_lvl)
86 if (WSAStartup(MAKEWORD(1, 1), &wsaData)) {
87 FGLogging log(LogLevel::ERROR);
88 log <<
"Winsock DLL not initialized ...\n";
93 FGLogging log(LogLevel::DEBUG);
94 log <<
"Winsock DLL loaded ...\n";
103 sckt = sckt_in = INVALID_SOCKET;
104 Protocol = (ProtocolType)protocol;
106 struct addrinfo *addr =
nullptr;
107 this->precision = precision;
110 if (!LoadWinSockDLL(debug_lvl))
return;
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;
119 hints.ai_socktype = SOCK_STREAM;
120 hints.ai_protocol = 0;
121 if (!is_number(address))
122 hints.ai_flags = AI_ADDRCONFIG;
124 hints.ai_flags = AI_NUMERICHOST;
126 int failure = getaddrinfo(address.c_str(), NULL, &hints, &addr);
127 if (failure || !addr) {
129 log <<
"Could not get host net address " << address;
131 if (hints.ai_flags == AI_NUMERICHOST)
132 log <<
" by number...\n";
134 log <<
" by name...\n";
136 log << gai_strerror(failure) <<
"\n";
142 sckt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
146 if (protocol == ptUDP)
147 log <<
"Creating UDP socket on port " << port <<
"\n";
149 log <<
"Creating TCP socket on port " << port <<
"\n";
152 if (sckt != INVALID_SOCKET) {
153 int len =
sizeof(
struct sockaddr_in);
154 memcpy(&scktName, addr->ai_addr, len);
155 scktName.sin_port = htons(port);
157 if (connect(sckt, (
struct sockaddr*)&scktName, len) == 0) {
160 log <<
"Successfully connected to socket for output ...\n";
165 log <<
"Could not connect to socket for output ...\n";
169 log <<
"Could not create socket for FDM output, error = " << errno <<
"\n";
181 sckt = INVALID_SOCKET;
183 Protocol = (ProtocolType)protocol;
185 this->precision = precision;
188 if (!LoadWinSockDLL(debug_lvl))
return;
191 if (Protocol == ptUDP) {
192 ProtocolName =
"UDP";
193 sckt = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
196 ioctlsocket(sckt, FIONBIO, &NonBlock);
198 int flags = fcntl(sckt, F_GETFL, 0);
199 fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
203 ProtocolName =
"TCP";
204 sckt = socket(AF_INET, SOCK_STREAM, 0);
209 log <<
"Creating input " << ProtocolName <<
" socket on port " << port
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);
218 if (Protocol == ptUDP)
219 scktName.sin_addr.s_addr = htonl(INADDR_ANY);
221 socklen_t len =
sizeof(
struct sockaddr_in);
222 if (bind(sckt, (
struct sockaddr*)&scktName, len) != SOCKET_ERROR) {
225 log <<
"Successfully bound to " << ProtocolName
226 <<
" input socket on port " << port <<
"\n\n";
229 if (Protocol == ptTCP) {
230 if (listen(sckt, 5) != SOCKET_ERROR) {
234 ioctlsocket(sckt, FIONBIO, &NoBlock);
236 int flags = fcntl(sckt, F_GETFL, 0);
237 fcntl(sckt, F_SETFL, flags | O_NONBLOCK);
239 sckt_in = accept(sckt, (
struct sockaddr*)&scktName, &len);
242 sckt = INVALID_SOCKET;
244 log <<
"Could not listen ...\n";
250 sckt = INVALID_SOCKET;
252 log <<
"Could not bind to " << ProtocolName <<
" input socket, error = "
257 log <<
"Could not create " << ProtocolName <<
" socket for input, error = "
266FGfdmSocket::~FGfdmSocket()
269 if (sckt_in != INVALID_SOCKET) shutdown(sckt_in, SD_BOTH);
270 if (sckt != INVALID_SOCKET) closesocket(sckt);
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) {
289 ioctlsocket(sckt_in, FIONBIO, &NoBlock);
291 int flags = fcntl(sckt_in, F_GETFL, 0);
292 fcntl(sckt_in, F_SETFL, flags | O_NONBLOCK);
294 if (send(sckt_in,
"Connected to JSBSim server\r\nJSBSim> ", 36, 0) == SOCKET_ERROR)
295 LogSocketError(
"Receive - TCP connection acknowledgement");
299 if (sckt_in != INVALID_SOCKET) {
302 while ((num_chars = recv(sckt_in, buf,
sizeof buf, 0)) > 0)
303 data.append(buf, num_chars);
305 if (num_chars == SOCKET_ERROR || num_chars == 0) {
307 if (WSAGetLastError() != WSAEWOULDBLOCK)
309 if (errno != EWOULDBLOCK)
312 LogSocketError(
"Receive - TCP data reception");
316 log <<
"Socket Closed. Back to listening\n";
317 closesocket(sckt_in);
318 sckt_in = INVALID_SOCKET;
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) {
332 if (WSAGetLastError() != WSAEWOULDBLOCK)
334 if (errno != EWOULDBLOCK)
336 LogSocketError(
"Receive - UDP data reception");
347 int num_chars_sent=0;
348 assert(Protocol == ptTCP);
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");
356 log <<
"Socket reply must be to a valid socket\n";
359 return num_chars_sent;
366 assert(Protocol == ptTCP);
367 closesocket(sckt_in);
368 sckt_in = INVALID_SOCKET;
390 if (buffer.tellp() > 0) buffer <<
',';
398 if (buffer.tellp() > 0) buffer <<
',';
399 buffer << std::setw(12) << std::setprecision(precision) << item;
406 if (buffer.tellp() > 0) buffer <<
',';
407 buffer << std::setw(12) << item;
415 string str = buffer.str();
416 Send(str.c_str(), str.size());
423 if (Protocol == ptTCP && sckt_in != INVALID_SOCKET) {
424 if ((send(sckt_in, data, length, 0)) == SOCKET_ERROR) LogSocketError(
"Send - TCP data sending");
428 if (Protocol == ptUDP && sckt != INVALID_SOCKET) {
429 if ((send(sckt, data, length, 0)) == SOCKET_ERROR) LogSocketError(
"Send - UDP data sending");
434 log <<
"Data sending must be to a valid socket\n";
441 assert(Protocol == ptTCP);
442 if (sckt_in == INVALID_SOCKET)
return;
446 FD_SET(sckt_in, &fds);
448 int result = select(FD_SETSIZE, &fds,
nullptr,
nullptr,
nullptr);
452 log <<
"Socket timeout.\n";
454 }
else if (result != SOCKET_ERROR)
457 LogSocketError(
"WaitUntilReadable");
462void FGfdmSocket::LogSocketError(
const std::string& msg)
466 log <<
"Socket error in " << msg <<
": ";
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);
475 log << strerror(errno) <<
"\n";
498void FGfdmSocket::Debug(
int from)
500 if (debug_lvl <= 0)
return;
506 if (debug_lvl & 2 ) {
507 FGLogging log(LogLevel::DEBUG);
508 if (from == 0) log <<
"Instantiated: FGfdmSocket\n";
509 if (from == 1) log <<
"Destroyed: FGfdmSocket\n";
511 if (debug_lvl & 4 ) {
513 if (debug_lvl & 8 ) {
515 if (debug_lvl & 16) {
517 if (debug_lvl & 64) {
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.