F´ Flight Software - C/C++ Documentation  NASA-v2.0.0
A framework for building embedded system applications to NASA flight quality standards.
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