F´ Flight Software - C/C++ Documentation  NASA-v2.0.1
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SocketHelper.cpp
Go to the documentation of this file.
1 /*
2  * SocketHelper.cpp
3  *
4  * Created on: May 28, 2020
5  * Author: tcanham
6  */
7 
9 #include <Fw/Types/Assert.hpp>
11 #include <Fw/Logger/Logger.hpp>
12 
13 // This implementation has primarily implemented to isolate
14 // the socket interface from the F' Fw::Buffer class.
15 // There is a macro in VxWorks (m_data) that collides with
16 // the m_data member in Fw::Buffer.
17 
18 
19 #ifdef TGT_OS_TYPE_VXWORKS
20  #include <socket.h>
21  #include <inetLib.h>
22  #include <fioLib.h>
23  #include <hostLib.h>
24  #include <ioLib.h>
25  #include <vxWorks.h>
26  #include <sockLib.h>
27  #include <fioLib.h>
28  #include <taskLib.h>
29  #include <sysLib.h>
30  #include <errnoLib.h>
31  #include <string.h>
32 #elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
33  #include <sys/socket.h>
34  #include <netdb.h>
35  #include <unistd.h>
36  #include <errno.h>
37  #include <arpa/inet.h>
38 #else
39  #error OS not supported for IP Socket Communications
40 #endif
41 
42 #include <stdio.h>
43 #include <string.h>
44 
45 
46 namespace Drv {
47 
48  // put network specific state we need to isolate here
49 
50  struct SocketState {
51  struct sockaddr_in m_udpAddr;
52  };
53 
55  ,m_socketInFd(-1)
56  ,m_socketOutFd(-1)
57  ,m_sendUdp(false)
58  ,m_timeoutSeconds(0)
59  ,m_timeoutMicroseconds(0)
60  ,m_port(0)
61  {
62  }
63 
65  delete this->m_state;
66  }
67 
69  const char* hostname,
70  const U16 port,
71  const bool send_udp,
72  const U32 timeout_seconds,
73  const U32 timeout_microseconds
74  ) {
75  this->m_sendUdp = send_udp;
76  this->m_timeoutSeconds = timeout_seconds;
77  this->m_timeoutSeconds = timeout_seconds;
78  this->m_port = port;
79  // copy hostname to local copy
80  (void)strncpy(this->m_hostname,hostname,MAX_HOSTNAME_SIZE);
81  // null terminate
82  this->m_hostname[MAX_HOSTNAME_SIZE-1] = 0;
83  return SOCK_SUCCESS;
84  }
85 
87  return (-1 != this->m_socketInFd);
88  }
89 
90  void SocketHelper::close(void) {
91  if (this->m_socketInFd != -1) {
92  (void) ::close(this->m_socketInFd);
93  }
94  this->m_socketInFd = -1;
95  this->m_socketOutFd = -1;
96  }
97 
99 
100  SocketIpStatus status = SOCK_SUCCESS;
101  // Only the input (TCP) socket needs closing
102  if (this->m_socketInFd != -1) {
103  (void) ::close(this->m_socketInFd); // Close open sockets, to force a re-open
104  }
105  this->m_socketInFd = -1;
106  // Open a TCP socket for incoming commands, and outgoing data if not using UDP
107  status = this->openProtocol(SOCK_STREAM);
108  if (status != SOCK_SUCCESS) {
109  return status;
110  }
111  // If we need UDP sending, attempt to open UDP
112  if (this->m_sendUdp && this->m_socketOutFd == -1 && (status = this->openProtocol(SOCK_DGRAM, false)) != SOCK_SUCCESS) {
113  (void) ::close(this->m_socketInFd);
114  return status;
115  }
116  // Not sending UDP, reuse our input TCP socket
117  else if (!this->m_sendUdp) {
118  this->m_socketOutFd = this->m_socketInFd;
119  }
120  // Coding error, if not successful here
121  FW_ASSERT(status == SOCK_SUCCESS);
122 
123  return status;
124 
125  }
126 
127  SocketIpStatus SocketHelper::openProtocol(NATIVE_INT_TYPE protocol, bool isInput) {
128 
129  NATIVE_INT_TYPE socketFd = -1;
130  struct sockaddr_in address;
131 
132  // Clear existing file descriptors
133  if (isInput and this->m_socketInFd != -1) {
134  (void) ::close(this->m_socketInFd);
135  this->m_socketInFd = -1;
136  } else if (not isInput and this->m_socketOutFd != -1) {
137  (void) ::close(this->m_socketOutFd);
138  this->m_socketOutFd = -1;
139  }
140  // Acquire a socket, or return error
141  if ((socketFd = ::socket(AF_INET, protocol, 0)) == -1) {
143  }
144  // Set up the address port and name
145  address.sin_family = AF_INET;
146  address.sin_port = htons(this->m_port);
147  #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
148  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
149  #endif
150  // Get the IP address from host
151  #ifdef TGT_OS_TYPE_VXWORKS
152  address.sin_addr.s_addr = inet_addr(this->m_hostname);
153  #else
154  // Set timeout socket option
155  struct timeval timeout;
156  timeout.tv_sec = this->m_timeoutSeconds;
157  timeout.tv_usec = this->m_timeoutMicroseconds;
158  // set socket write to timeout after 1 sec
159  if (setsockopt(socketFd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
160  (void) ::close(socketFd);
162  }
163  // Get possible IP addresses
164  struct hostent *host_entry;
165  if ((host_entry = gethostbyname(this->m_hostname)) == NULL || host_entry->h_addr_list[0] == NULL) {
166  ::close(socketFd);
168  }
169  // First IP address to socket sin_addr
170  if (inet_pton(address.sin_family, m_hostname, &(address.sin_addr)) < 0) {
171  ::close(socketFd);
173  };
174  #endif
175  // If TCP, connect to the socket to allow for communication
176  if (protocol != SOCK_DGRAM && connect(socketFd, reinterpret_cast<struct sockaddr *>(&address),
177  sizeof(address)) < 0) {
178  ::close(socketFd);
179  return SOCK_FAILED_TO_CONNECT;
180  }
181  // Store the UDP address for later
182  else if (protocol == SOCK_DGRAM) {
183  FW_ASSERT(sizeof(this->m_state->m_udpAddr) == sizeof(address), sizeof(this->m_state->m_udpAddr), sizeof(address));
184  memcpy(&this->m_state->m_udpAddr, &address, sizeof(this->m_state->m_udpAddr));
185  }
186 
187  // Set the member socket variable
188  if (isInput) {
189  this->m_socketInFd = socketFd;
190  } else {
191  this->m_socketOutFd = socketFd;
192  }
193  Fw::Logger::logMsg("Connected to %s:%hu for %s using %s\n", reinterpret_cast<POINTER_CAST>(m_hostname), m_port,
194  reinterpret_cast<POINTER_CAST>(isInput ? "uplink" : "downlink"),
195  reinterpret_cast<POINTER_CAST>((protocol == SOCK_DGRAM) ? "udp" : "tcp"));
196  return SOCK_SUCCESS;
197 
198  }
199 
200  void SocketHelper::send(U8* data, const U32 size) {
201 
202  U32 total = 0;
203  // Prevent transmission before connection, or after a disconnect
204  if (this->m_socketOutFd == -1) {
205  return;
206  }
207  // Attempt to send out data
208  for (U32 i = 0; i < MAX_SEND_ITERATIONS && total < size; i++) {
209  I32 sent = 0;
210  // Output to send UDP
211  if (this->m_sendUdp) {
212  sent = ::sendto(this->m_socketOutFd, data + total, size - total, SOCKET_SEND_FLAGS,
213  reinterpret_cast<struct sockaddr *>(&this->m_state->m_udpAddr), sizeof(this->m_state->m_udpAddr));
214  }
215  // Output to send TCP
216  else {
217  sent = ::send(this->m_socketOutFd, data + total, size - total, SOCKET_SEND_FLAGS);
218  }
219  // Error is EINTR, just try again
220  if (sent == -1 && errno == EINTR) {
221  continue;
222  }
223  // Error bad file descriptor is a close
224  else if (sent == -1 && errno == EBADF) {
225  Fw::Logger::logMsg("[ERROR] Server disconnected\n");
226  this->close();
227  this->m_socketOutFd = -1;
228  break;
229  }
230  // Error returned, and it wasn't an interrupt
231  else if (sent == -1 && errno != EINTR) {
232  Fw::Logger::logMsg("[ERROR] IP send failed ERRNO: %d UDP: %d\n", errno, m_sendUdp);
233  break;
234  }
235  // Successful write
236  else {
237  total += sent;
238  }
239  }
240 
241  }
242 
244 
245  SocketIpStatus status = SOCK_SUCCESS;
246  // Attempt to recv out data
247  size = ::recv(m_socketInFd, data, MAX_RECV_BUFFER_SIZE, SOCKET_RECV_FLAGS);
248  // Error returned, and it wasn't an interrupt, nor a reset
249  if (size == -1 && errno != EINTR && errno != ECONNRESET) {
250  Fw::Logger::logMsg("[ERROR] IP recv failed ERRNO: %d\n", errno);
251  status = SOCK_READ_ERROR; // Stop recv task on error
252  }
253  // Error is EINTR, just try again
254  else if (size == -1 && errno == EINTR) {
256  }
257  // Zero bytes read means a closed socket
258  else if (size == 0 || errno == ECONNRESET) {
259  status = SOCK_READ_DISCONNECTED;
260  }
261 
262  return status;
263 
264  }
265 
266 }
Drv::SOCK_SUCCESS
@ SOCK_SUCCESS
Socket operation successful.
Definition: IpSocket.hpp:24
Drv::SOCK_INTERRUPTED_TRY_AGAIN
@ SOCK_INTERRUPTED_TRY_AGAIN
Interrupted status for retries.
Definition: IpSocket.hpp:30
Drv::SocketHelper::open
SocketIpStatus open(void)
Definition: SocketHelper.cpp:98
SOCKET_SEND_FLAGS
@ SOCKET_SEND_FLAGS
Definition: SocketIpDriverCfg.hpp:22
Drv::SOCK_FAILED_TO_SET_SOCKET_OPTIONS
@ SOCK_FAILED_TO_SET_SOCKET_OPTIONS
Failed to configure socket.
Definition: IpSocket.hpp:29
SocketHelper.hpp
U8
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.hpp:76
Drv::SOCK_FAILED_TO_CONNECT
@ SOCK_FAILED_TO_CONNECT
Failed to connect socket.
Definition: IpSocket.hpp:28
Drv
Definition: BlockDriverImpl.cpp:5
Drv::SOCK_INVALID_IP_ADDRESS
@ SOCK_INVALID_IP_ADDRESS
Bad IP address supplied.
Definition: IpSocket.hpp:27
Drv::SocketState::m_udpAddr
struct sockaddr_in m_udpAddr
UDP server address, maybe unused.
Definition: SocketHelper.cpp:51
Assert.hpp
Drv::SOCK_READ_DISCONNECTED
@ SOCK_READ_DISCONNECTED
Definition: SocketIpDriverTypes.hpp:31
Drv::SOCK_FAILED_TO_GET_SOCKET
@ SOCK_FAILED_TO_GET_SOCKET
Socket open failed.
Definition: IpSocket.hpp:25
Drv::SocketState
Definition: UdpSocket.cpp:42
MAX_HOSTNAME_SIZE
@ MAX_HOSTNAME_SIZE
Definition: SocketIpDriverCfg.hpp:28
Drv::SOCK_FAILED_TO_GET_HOST_IP
@ SOCK_FAILED_TO_GET_HOST_IP
Host IP lookup failed.
Definition: IpSocket.hpp:26
MAX_SEND_ITERATIONS
@ MAX_SEND_ITERATIONS
Definition: SocketIpDriverCfg.hpp:25
FW_ASSERT
#define FW_ASSERT(...)
Definition: Assert.hpp:9
Drv::SocketHelper::configure
SocketIpStatus configure(const char *hostname, const U16 port, const bool send_udp, const U32 timeout_seconds, const U32 timeout_microseconds)
Definition: SocketHelper.cpp:68
Fw::Logger::logMsg
static void logMsg(const char *fmt, POINTER_CAST a0=0, POINTER_CAST a1=0, POINTER_CAST a2=0, POINTER_CAST a3=0, POINTER_CAST a4=0, POINTER_CAST a5=0, POINTER_CAST a6=0, POINTER_CAST a7=0, POINTER_CAST a8=0, POINTER_CAST a9=0)
Definition: Logger.cpp:18
Drv::SocketHelper::isOpened
bool isOpened(void)
Definition: SocketHelper.cpp:86
Drv::SocketIpStatus
SocketIpStatus
Status enumeration for socket return values.
Definition: IpSocket.hpp:23
Drv::SocketHelper::close
void close(void)
Definition: SocketHelper.cpp:90
SOCKET_RECV_FLAGS
@ SOCKET_RECV_FLAGS
Definition: SocketIpDriverCfg.hpp:23
Drv::SocketHelper::send
void send(U8 *data, const U32 size)
Definition: SocketHelper.cpp:200
Drv::SocketHelper::SocketHelper
SocketHelper()
Definition: SocketHelper.cpp:54
Drv::SocketHelper::recv
SocketIpStatus recv(U8 *data, I32 &size)
Definition: SocketHelper.cpp:243
BasicTypes.hpp
Declares ISF basic types.
Drv::SOCK_READ_ERROR
@ SOCK_READ_ERROR
Failed to read socket.
Definition: IpSocket.hpp:31
Drv::SocketHelper::~SocketHelper
virtual ~SocketHelper()
Definition: SocketHelper.cpp:64
NATIVE_INT_TYPE
int NATIVE_INT_TYPE
native integer type declaration
Definition: BasicTypes.hpp:29
NULL
#define NULL
NULL.
Definition: BasicTypes.hpp:100
Logger.hpp
MAX_RECV_BUFFER_SIZE
@ MAX_RECV_BUFFER_SIZE
Definition: SocketIpDriverCfg.hpp:26