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