mirror of
https://github.com/nasa/fprime.git
synced 2025-12-10 17:47:10 -06:00
Correcting shutdown/close usage; refactoring TcpServer (#2950)
* Correcting shutdown/close usage; refactoring TcpServer * Fixing Tcp * Review and CI fixes * ...and the other half of the fixes
This commit is contained in:
parent
c7e68c52ed
commit
9ed68fdd06
@ -23,17 +23,17 @@
|
|||||||
|
|
||||||
#ifdef TGT_OS_TYPE_VXWORKS
|
#ifdef TGT_OS_TYPE_VXWORKS
|
||||||
#include <socket.h>
|
#include <socket.h>
|
||||||
#include <inetLib.h>
|
#include <inetLib.h>
|
||||||
#include <fioLib.h>
|
#include <fioLib.h>
|
||||||
#include <hostLib.h>
|
#include <hostLib.h>
|
||||||
#include <ioLib.h>
|
#include <ioLib.h>
|
||||||
#include <vxWorks.h>
|
#include <vxWorks.h>
|
||||||
#include <sockLib.h>
|
#include <sockLib.h>
|
||||||
#include <fioLib.h>
|
#include <fioLib.h>
|
||||||
#include <taskLib.h>
|
#include <taskLib.h>
|
||||||
#include <sysLib.h>
|
#include <sysLib.h>
|
||||||
#include <errnoLib.h>
|
#include <errnoLib.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
|
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -52,7 +52,7 @@ IpSocket::IpSocket() : m_timeoutSeconds(0), m_timeoutMicroseconds(0), m_port(0)
|
|||||||
|
|
||||||
SocketIpStatus IpSocket::configure(const char* const hostname, const U16 port, const U32 timeout_seconds, const U32 timeout_microseconds) {
|
SocketIpStatus IpSocket::configure(const char* const hostname, const U16 port, const U32 timeout_seconds, const U32 timeout_microseconds) {
|
||||||
FW_ASSERT(timeout_microseconds < 1000000, static_cast<FwAssertArgType>(timeout_microseconds));
|
FW_ASSERT(timeout_microseconds < 1000000, static_cast<FwAssertArgType>(timeout_microseconds));
|
||||||
FW_ASSERT(this->isValidPort(port));
|
FW_ASSERT(this->isValidPort(port), static_cast<FwAssertArgType>(port));
|
||||||
FW_ASSERT(hostname != nullptr);
|
FW_ASSERT(hostname != nullptr);
|
||||||
this->m_timeoutSeconds = timeout_seconds;
|
this->m_timeoutSeconds = timeout_seconds;
|
||||||
this->m_timeoutMicroseconds = timeout_microseconds;
|
this->m_timeoutMicroseconds = timeout_microseconds;
|
||||||
@ -65,7 +65,7 @@ bool IpSocket::isValidPort(U16 port) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus IpSocket::setupTimeouts(NATIVE_INT_TYPE socketFd) {
|
SocketIpStatus IpSocket::setupTimeouts(PlatformIntType socketFd) {
|
||||||
// Get the IP address from host
|
// Get the IP address from host
|
||||||
#ifdef TGT_OS_TYPE_VXWORKS
|
#ifdef TGT_OS_TYPE_VXWORKS
|
||||||
// No timeouts set on Vxworks
|
// No timeouts set on Vxworks
|
||||||
@ -103,38 +103,39 @@ SocketIpStatus IpSocket::addressToIp4(const char* address, void* ip4) {
|
|||||||
return SOCK_SUCCESS;
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpSocket::close(NATIVE_INT_TYPE fd) {
|
void IpSocket::close(const SocketDescriptor& socketDescriptor) {
|
||||||
(void)::shutdown(fd, SHUT_RDWR);
|
(void)::close(socketDescriptor.fd);
|
||||||
(void)::close(fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpSocket::shutdown(NATIVE_INT_TYPE fd) {
|
void IpSocket::shutdown(const SocketDescriptor& socketDescriptor) {
|
||||||
this->close(fd);
|
errno = 0;
|
||||||
|
PlatformIntType status = ::shutdown(socketDescriptor.fd, SHUT_RDWR);
|
||||||
|
// If shutdown fails, go straight to the hard-shutdown
|
||||||
|
if (status != 0) {
|
||||||
|
this->close(socketDescriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus IpSocket::startup() {
|
SocketIpStatus IpSocket::open(SocketDescriptor& socketDescriptor) {
|
||||||
// no op for non-server components
|
|
||||||
return SOCK_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIpStatus IpSocket::open(NATIVE_INT_TYPE& fd) {
|
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
|
errno = 0;
|
||||||
// Open a TCP socket for incoming commands, and outgoing data if not using UDP
|
// Open a TCP socket for incoming commands, and outgoing data if not using UDP
|
||||||
status = this->openProtocol(fd);
|
status = this->openProtocol(socketDescriptor);
|
||||||
if (status != SOCK_SUCCESS) {
|
if (status != SOCK_SUCCESS) {
|
||||||
FW_ASSERT(fd == -1); // Ensure we properly kept closed on error
|
socketDescriptor.fd = -1;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus IpSocket::send(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) {
|
SocketIpStatus IpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||||
U32 total = 0;
|
U32 total = 0;
|
||||||
I32 sent = 0;
|
I32 sent = 0;
|
||||||
// Attempt to send out data and retry as necessary
|
// Attempt to send out data and retry as necessary
|
||||||
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) {
|
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (total < size); i++) {
|
||||||
|
errno = 0;
|
||||||
// Send using my specific protocol
|
// Send using my specific protocol
|
||||||
sent = this->sendProtocol(fd, data + total, size - total);
|
sent = this->sendProtocol(socketDescriptor, data + total, size - total);
|
||||||
// Error is EINTR or timeout just try again
|
// Error is EINTR or timeout just try again
|
||||||
if (((sent == -1) && (errno == EINTR)) || (sent == 0)) {
|
if (((sent == -1) && (errno == EINTR)) || (sent == 0)) {
|
||||||
continue;
|
continue;
|
||||||
@ -159,13 +160,13 @@ SocketIpStatus IpSocket::send(NATIVE_INT_TYPE fd, const U8* const data, const U3
|
|||||||
return SOCK_SUCCESS;
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus IpSocket::recv(NATIVE_INT_TYPE fd, U8* data, U32& req_read) {
|
SocketIpStatus IpSocket::recv(const SocketDescriptor& socketDescriptor, U8* data, U32& req_read) {
|
||||||
I32 size = 0;
|
I32 size = 0;
|
||||||
|
|
||||||
// Try to read until we fail to receive data
|
// Try to read until we fail to receive data
|
||||||
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (size <= 0); i++) {
|
for (U32 i = 0; (i < SOCKET_MAX_ITERATIONS) && (size <= 0); i++) {
|
||||||
|
errno = 0;
|
||||||
// Attempt to recv out data
|
// Attempt to recv out data
|
||||||
size = this->recvProtocol(fd, data, req_read);
|
size = this->recvProtocol(socketDescriptor, data, req_read);
|
||||||
|
|
||||||
// Nothing to be received
|
// Nothing to be received
|
||||||
if ((size == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
|
if ((size == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
|
||||||
|
|||||||
@ -17,6 +17,12 @@
|
|||||||
#include <Os/Mutex.hpp>
|
#include <Os/Mutex.hpp>
|
||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
|
struct SocketDescriptor final {
|
||||||
|
PlatformIntType fd = -1; //!< Used for all sockets to track the communication file descriptor
|
||||||
|
PlatformIntType serverFd = -1; //!< Used for server sockets to track the listening file descriptor
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Status enumeration for socket return values
|
* \brief Status enumeration for socket return values
|
||||||
*/
|
*/
|
||||||
@ -36,7 +42,8 @@ enum SocketIpStatus {
|
|||||||
SOCK_SEND_ERROR = -13, //!< Failed to send after configured retries
|
SOCK_SEND_ERROR = -13, //!< Failed to send after configured retries
|
||||||
SOCK_NOT_STARTED = -14, //!< Socket has not been started
|
SOCK_NOT_STARTED = -14, //!< Socket has not been started
|
||||||
SOCK_FAILED_TO_READ_BACK_PORT = -15, //!< Failed to read back port from connection
|
SOCK_FAILED_TO_READ_BACK_PORT = -15, //!< Failed to read back port from connection
|
||||||
SOCK_NO_DATA_AVAILABLE = -16 //!< No data available or read operation would block
|
SOCK_NO_DATA_AVAILABLE = -16, //!< No data available or read operation would block
|
||||||
|
SOCK_ANOTHER_THREAD_OPENING = -17 //!< Another thread is opening
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,16 +77,6 @@ class IpSocket {
|
|||||||
SocketIpStatus configure(const char* hostname, const U16 port, const U32 send_timeout_seconds,
|
SocketIpStatus configure(const char* hostname, const U16 port, const U32 send_timeout_seconds,
|
||||||
const U32 send_timeout_microseconds);
|
const U32 send_timeout_microseconds);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief startup the socket, a no-op on unless this is server
|
|
||||||
*
|
|
||||||
* This will start-up the socket. In the case of most sockets, this is a no-op. On server sockets this binds to the
|
|
||||||
* server address and progresses through the `listen` step such that on `open` new clients may be accepted.
|
|
||||||
*
|
|
||||||
* \return status of startup
|
|
||||||
*/
|
|
||||||
virtual SocketIpStatus startup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief open the IP socket for communications
|
* \brief open the IP socket for communications
|
||||||
*
|
*
|
||||||
@ -95,10 +92,10 @@ class IpSocket {
|
|||||||
*
|
*
|
||||||
* Note: delegates to openProtocol for protocol specific implementation
|
* Note: delegates to openProtocol for protocol specific implementation
|
||||||
*
|
*
|
||||||
* \param fd: file descriptor to open
|
* \param socketDescriptor: socket descriptor to update with opened port
|
||||||
* \return status of open
|
* \return status of open
|
||||||
*/
|
*/
|
||||||
SocketIpStatus open(NATIVE_INT_TYPE& fd);
|
SocketIpStatus open(SocketDescriptor& socketDescriptor);
|
||||||
/**
|
/**
|
||||||
* \brief send data out the IP socket from the given buffer
|
* \brief send data out the IP socket from the given buffer
|
||||||
*
|
*
|
||||||
@ -115,7 +112,7 @@ class IpSocket {
|
|||||||
* \param size: size of data to send
|
* \param size: size of data to send
|
||||||
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
||||||
*/
|
*/
|
||||||
SocketIpStatus send(NATIVE_INT_TYPE fd, const U8* const data, const U32 size);
|
SocketIpStatus send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size);
|
||||||
/**
|
/**
|
||||||
* \brief receive data from the IP socket from the given buffer
|
* \brief receive data from the IP socket from the given buffer
|
||||||
*
|
*
|
||||||
@ -127,31 +124,35 @@ class IpSocket {
|
|||||||
*
|
*
|
||||||
* Note: delegates to `recvProtocol` to send the data
|
* Note: delegates to `recvProtocol` to send the data
|
||||||
*
|
*
|
||||||
* \param fd: file descriptor to recv from
|
* \param socketDescriptor: socket descriptor to recv from
|
||||||
* \param data: pointer to data to fill with received data
|
* \param data: pointer to data to fill with received data
|
||||||
* \param size: maximum size of data buffer to fill
|
* \param size: maximum size of data buffer to fill
|
||||||
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
* \return status of the send, SOCK_DISCONNECTED to reopen, SOCK_SUCCESS on success, something else on error
|
||||||
*/
|
*/
|
||||||
SocketIpStatus recv(NATIVE_INT_TYPE fd, U8* const data, U32& size);
|
SocketIpStatus recv(const SocketDescriptor& fd, U8* const data, U32& size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief closes the socket
|
* \brief closes the socket
|
||||||
*
|
*
|
||||||
* Closes the socket opened by the open call. In this case of the TcpServer, this does NOT close server's listening
|
* Closes the socket opened by the open call. In this case of the TcpServer, this does NOT close server's listening
|
||||||
* port (call `shutdown`) but will close the active client connection.
|
* port but will close the active client connection.
|
||||||
*
|
*
|
||||||
* \param fd: file descriptor to close
|
* \param socketDescriptor: socket descriptor to close
|
||||||
*/
|
*/
|
||||||
void close(NATIVE_INT_TYPE fd);
|
void close(const SocketDescriptor& socketDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief shutdown the socket
|
* \brief shutdown the socket
|
||||||
*
|
*
|
||||||
* Closes the socket opened by the open call. In this case of the TcpServer, this does close server's listening
|
* Shuts down the socket opened by the open call. In this case of the TcpServer, this does shut down server's
|
||||||
* port. This will shutdown all clients.
|
* listening port, but rather shuts down the active client.
|
||||||
*
|
*
|
||||||
* \param fd: file descriptor to shutdown
|
* A shut down begins the termination of communication. The underlying socket will coordinate a clean shutdown, and
|
||||||
|
* it is safe to close the socket once a recv with 0 size has returned or an appropriate timeout has been reached.
|
||||||
|
*
|
||||||
|
* \param socketDescriptor: socket descriptor to shutdown
|
||||||
*/
|
*/
|
||||||
virtual void shutdown(NATIVE_INT_TYPE fd);
|
void shutdown(const SocketDescriptor& socketDescriptor);
|
||||||
|
|
||||||
PROTECTED:
|
PROTECTED:
|
||||||
/**
|
/**
|
||||||
@ -168,10 +169,10 @@ class IpSocket {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief setup the socket timeout properties of the opened outgoing socket
|
* \brief setup the socket timeout properties of the opened outgoing socket
|
||||||
* \param socketFd: file descriptor to setup
|
* \param socketDescriptor: socket descriptor to setup
|
||||||
* \return status of timeout setup
|
* \return status of timeout setup
|
||||||
*/
|
*/
|
||||||
SocketIpStatus setupTimeouts(NATIVE_INT_TYPE socketFd);
|
SocketIpStatus setupTimeouts(PlatformIntType socketFd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief converts a given address in dot form x.x.x.x to an ip address. ONLY works for IPv4.
|
* \brief converts a given address in dot form x.x.x.x to an ip address. ONLY works for IPv4.
|
||||||
@ -182,27 +183,27 @@ class IpSocket {
|
|||||||
static SocketIpStatus addressToIp4(const char* address, void* ip4);
|
static SocketIpStatus addressToIp4(const char* address, void* ip4);
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific open implementation, called from open.
|
* \brief Protocol specific open implementation, called from open.
|
||||||
* \param fd: (output) file descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
* \param socketDescriptor: (output) socket descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
||||||
* \return status of open
|
* \return status of open
|
||||||
*/
|
*/
|
||||||
virtual SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) = 0;
|
virtual SocketIpStatus openProtocol(SocketDescriptor& fd) = 0;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
||||||
* \param fd: file descriptor to send to
|
* \param socketDescriptor: socket descriptor to send to
|
||||||
* \param data: data to send
|
* \param data: data to send
|
||||||
* \param size: size of data to send
|
* \param size: size of data to send
|
||||||
* \return: size of data sent, or -1 on error.
|
* \return: size of data sent, or -1 on error.
|
||||||
*/
|
*/
|
||||||
virtual I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) = 0;
|
virtual I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||||
* \param fd: file descriptor to recv from
|
* \param socket: socket descriptor to recv from
|
||||||
* \param data: data pointer to fill
|
* \param data: data pointer to fill
|
||||||
* \param size: size of data buffer
|
* \param size: size of data buffer
|
||||||
* \return: size of data received, or -1 on error.
|
* \return: size of data received, or -1 on error.
|
||||||
*/
|
*/
|
||||||
virtual I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) = 0;
|
virtual I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) = 0;
|
||||||
|
|
||||||
U32 m_timeoutSeconds;
|
U32 m_timeoutSeconds;
|
||||||
U32 m_timeoutMicroseconds;
|
U32 m_timeoutMicroseconds;
|
||||||
|
|||||||
@ -15,11 +15,9 @@
|
|||||||
#include <Fw/Types/Assert.hpp>
|
#include <Fw/Types/Assert.hpp>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#define MAXIMUM_SIZE 0x7FFFFFFF
|
|
||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
SocketComponentHelper::SocketComponentHelper() : m_fd(-1), m_reconnect(false), m_stop(false), m_started(false), m_open(false) {}
|
SocketComponentHelper::SocketComponentHelper() {}
|
||||||
|
|
||||||
SocketComponentHelper::~SocketComponentHelper() {}
|
SocketComponentHelper::~SocketComponentHelper() {}
|
||||||
|
|
||||||
@ -29,61 +27,64 @@ void SocketComponentHelper::start(const Fw::StringBase &name,
|
|||||||
const Os::Task::ParamType stack,
|
const Os::Task::ParamType stack,
|
||||||
const Os::Task::ParamType cpuAffinity) {
|
const Os::Task::ParamType cpuAffinity) {
|
||||||
FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
|
||||||
FW_ASSERT(not this->m_stop); // It is a coding error to stop the thread before it is started
|
this->m_stop = false;
|
||||||
m_reconnect = reconnect;
|
m_reconnect = reconnect;
|
||||||
// Note: the first step is for the IP socket to open the port
|
// Note: the first step is for the IP socket to open the port
|
||||||
Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
|
Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
|
||||||
Os::Task::Status stat = m_task.start(arguments);
|
Os::Task::Status stat = m_task.start(arguments);
|
||||||
FW_ASSERT(Os::Task::OP_OK == stat, static_cast<NATIVE_INT_TYPE>(stat));
|
FW_ASSERT(Os::Task::OP_OK == stat, static_cast<FwAssertArgType>(stat));
|
||||||
}
|
|
||||||
|
|
||||||
SocketIpStatus SocketComponentHelper::startup() {
|
|
||||||
this->m_started = true;
|
|
||||||
return this->getSocketHandler().startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SocketComponentHelper::isStarted() {
|
|
||||||
bool is_started = false;
|
|
||||||
is_started = this->m_started;
|
|
||||||
return is_started;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus SocketComponentHelper::open() {
|
SocketIpStatus SocketComponentHelper::open() {
|
||||||
SocketIpStatus status = SOCK_FAILED_TO_GET_SOCKET;
|
SocketIpStatus status = SOCK_ANOTHER_THREAD_OPENING;
|
||||||
NATIVE_INT_TYPE fd = -1;
|
OpenState local_open = OpenState::OPEN;
|
||||||
this->m_lock.lock();
|
// Scope to guard lock
|
||||||
if (not this->m_open) {
|
{
|
||||||
FW_ASSERT(this->m_fd == -1 and not this->m_open); // Ensure we are not opening an opened socket
|
Os::ScopeLock scopeLock(m_lock);
|
||||||
status = this->getSocketHandler().open(fd);
|
if (this->m_open == OpenState::NOT_OPEN) {
|
||||||
this->m_fd = fd;
|
this->m_open = OpenState::OPENING;
|
||||||
// Call connected any time the open is successful
|
local_open = this->m_open;
|
||||||
|
} else {
|
||||||
|
local_open = OpenState::SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (local_open == OpenState::OPENING) {
|
||||||
|
FW_ASSERT(this->m_descriptor.fd == -1); // Ensure we are not opening an opened socket
|
||||||
|
status = this->getSocketHandler().open(this->m_descriptor);
|
||||||
|
// Lock scope
|
||||||
|
{
|
||||||
|
Os::ScopeLock scopeLock(m_lock);
|
||||||
|
if (Drv::SOCK_SUCCESS == status) {
|
||||||
|
this->m_open = OpenState::OPEN;
|
||||||
|
} else {
|
||||||
|
this->m_open = OpenState::NOT_OPEN;
|
||||||
|
this->m_descriptor.fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Notify connection on success outside locked scope
|
||||||
if (Drv::SOCK_SUCCESS == status) {
|
if (Drv::SOCK_SUCCESS == status) {
|
||||||
this->m_open = true;
|
|
||||||
this->m_lock.unlock();
|
|
||||||
this->connected();
|
this->connected();
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->m_lock.unlock();
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SocketComponentHelper::isOpened() {
|
bool SocketComponentHelper::isOpened() {
|
||||||
bool is_open = false;
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
this->m_lock.lock();
|
bool is_open = this->m_open == OpenState::OPEN;
|
||||||
is_open = this->m_open;
|
|
||||||
this->m_lock.unlock();
|
|
||||||
return is_open;
|
return is_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus SocketComponentHelper::reconnect() {
|
SocketIpStatus SocketComponentHelper::reconnect() {
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
|
|
||||||
// Handle opening
|
|
||||||
|
|
||||||
// Open a network connection if it has not already been open
|
// Open a network connection if it has not already been open
|
||||||
if (this->isStarted() and (not this->isOpened())) {
|
if (not this->isOpened()) {
|
||||||
status = this->open();
|
status = this->open();
|
||||||
|
if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
|
||||||
|
status = SocketIpStatus::SOCK_SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -91,20 +92,21 @@ SocketIpStatus SocketComponentHelper::reconnect() {
|
|||||||
SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size) {
|
SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size) {
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
this->m_lock.lock();
|
this->m_lock.lock();
|
||||||
NATIVE_INT_TYPE fd = this->m_fd;
|
SocketDescriptor descriptor = this->m_descriptor;
|
||||||
this->m_lock.unlock();
|
this->m_lock.unlock();
|
||||||
// Prevent transmission before connection, or after a disconnect
|
// Prevent transmission before connection, or after a disconnect
|
||||||
if (fd == -1) {
|
if (descriptor.fd == -1) {
|
||||||
status = this->reconnect();
|
status = this->reconnect();
|
||||||
// if reconnect wasn't successful, pass the that up to the caller
|
// if reconnect wasn't successful, pass the that up to the caller
|
||||||
if(status != SOCK_SUCCESS) {
|
if(status != SOCK_SUCCESS) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
// Refresh local copy after reconnect
|
||||||
this->m_lock.lock();
|
this->m_lock.lock();
|
||||||
fd = this->m_fd;
|
descriptor = this->m_descriptor;
|
||||||
this->m_lock.unlock();
|
this->m_lock.unlock();
|
||||||
}
|
}
|
||||||
status = this->getSocketHandler().send(fd, data, size);
|
status = this->getSocketHandler().send(descriptor, data, size);
|
||||||
if (status == SOCK_DISCONNECTED) {
|
if (status == SOCK_DISCONNECTED) {
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
@ -112,21 +114,15 @@ SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SocketComponentHelper::shutdown() {
|
void SocketComponentHelper::shutdown() {
|
||||||
this->m_lock.lock();
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
this->getSocketHandler().shutdown(this->m_fd);
|
this->getSocketHandler().shutdown(this->m_descriptor);
|
||||||
this->m_started = false;
|
|
||||||
this->m_fd = -1;
|
|
||||||
this->m_lock.unLock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketComponentHelper::close() {
|
void SocketComponentHelper::close() {
|
||||||
this->m_lock.lock();
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
if (this->m_fd != -1) {
|
this->getSocketHandler().close(this->m_descriptor);
|
||||||
this->getSocketHandler().close(this->m_fd);
|
this->m_descriptor.fd = -1;
|
||||||
this->m_fd = -1;
|
this->m_open = OpenState::NOT_OPEN;
|
||||||
}
|
|
||||||
this->m_open = false;
|
|
||||||
this->m_lock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Os::Task::Status SocketComponentHelper::join() {
|
Os::Task::Status SocketComponentHelper::join() {
|
||||||
@ -134,69 +130,79 @@ Os::Task::Status SocketComponentHelper::join() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SocketComponentHelper::stop() {
|
void SocketComponentHelper::stop() {
|
||||||
this->m_lock.lock();
|
// Scope to protect lock
|
||||||
this->m_stop = true;
|
{
|
||||||
this->m_lock.unlock();
|
Os::ScopeLock scopeLock(m_lock);
|
||||||
|
this->m_stop = true;
|
||||||
|
}
|
||||||
this->shutdown(); // Break out of any receives and fully shutdown
|
this->shutdown(); // Break out of any receives and fully shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SocketComponentHelper::running() {
|
||||||
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
|
bool running = not this->m_stop;
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
SocketIpStatus SocketComponentHelper::recv(U8* data, U32 &size) {
|
SocketIpStatus SocketComponentHelper::recv(U8* data, U32 &size) {
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
// Check for previously disconnected socket
|
// Check for previously disconnected socket
|
||||||
this->m_lock.lock();
|
this->m_lock.lock();
|
||||||
NATIVE_INT_TYPE fd = this->m_fd;
|
SocketDescriptor descriptor = this->m_descriptor;
|
||||||
this->m_lock.unlock();
|
this->m_lock.unlock();
|
||||||
if (fd == -1) {
|
if (descriptor.fd == -1) {
|
||||||
return SOCK_DISCONNECTED;
|
return SOCK_DISCONNECTED;
|
||||||
}
|
}
|
||||||
status = this->getSocketHandler().recv(fd, data, size);
|
status = this->getSocketHandler().recv(descriptor, data, size);
|
||||||
if (status == SOCK_DISCONNECTED) {
|
if (status == SOCK_DISCONNECTED) {
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketComponentHelper::readTask(void* pointer) {
|
void SocketComponentHelper::readLoop() {
|
||||||
FW_ASSERT(pointer);
|
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
|
||||||
do {
|
do {
|
||||||
// Prevent transmission before connection, or after a disconnect
|
// Prevent transmission before connection, or after a disconnect
|
||||||
if ((not self->isOpened()) and (not self->m_stop)) {
|
if ((not this->isOpened()) and this->running()) {
|
||||||
status = self->reconnect();
|
status = this->reconnect();
|
||||||
if(status != SOCK_SUCCESS) {
|
if (status != SOCK_SUCCESS) {
|
||||||
Fw::Logger::log(
|
Fw::Logger::log("[WARNING] Failed to open port with status %d and errno %d\n", status, errno);
|
||||||
"[WARNING] Failed to open port with status %d and errno %d\n",
|
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
||||||
status,
|
|
||||||
errno);
|
|
||||||
(void) Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the network connection is open, read from it
|
// If the network connection is open, read from it
|
||||||
if (self->isStarted() and self->isOpened() and (not self->m_stop)) {
|
if (this->isOpened() and this->running()) {
|
||||||
Fw::Buffer buffer = self->getBuffer();
|
Fw::Buffer buffer = this->getBuffer();
|
||||||
U8* data = buffer.getData();
|
U8* data = buffer.getData();
|
||||||
FW_ASSERT(data);
|
FW_ASSERT(data);
|
||||||
U32 size = buffer.getSize();
|
U32 size = buffer.getSize();
|
||||||
// recv blocks, so it may have been a while since its done an isOpened check
|
// recv blocks, so it may have been a while since its done an isOpened check
|
||||||
status = self->recv(data, size);
|
status = this->recv(data, size);
|
||||||
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN && (status != SOCK_NO_DATA_AVAILABLE))) {
|
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) && (status != SOCK_NO_DATA_AVAILABLE)) {
|
||||||
Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n",
|
Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n",
|
||||||
status,
|
status,
|
||||||
errno);
|
errno);
|
||||||
self->close();
|
this->close();
|
||||||
buffer.setSize(0);
|
buffer.setSize(0);
|
||||||
} else {
|
} else {
|
||||||
// Send out received data
|
// Send out received data
|
||||||
buffer.setSize(size);
|
buffer.setSize(size);
|
||||||
}
|
}
|
||||||
self->sendBuffer(buffer, status);
|
this->sendBuffer(buffer, status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// As long as not told to stop, and we are successful interrupted or ordered to retry, keep receiving
|
// As long as not told to stop, and we are successful interrupted or ordered to retry, keep receiving
|
||||||
while (not self->m_stop &&
|
while (this->running() &&
|
||||||
(status == SOCK_SUCCESS || status == SOCK_INTERRUPTED_TRY_AGAIN || self->m_reconnect));
|
(status == SOCK_SUCCESS || (status == SOCK_NO_DATA_AVAILABLE) || status == SOCK_INTERRUPTED_TRY_AGAIN || this->m_reconnect));
|
||||||
self->shutdown(); // Shutdown the port entirely
|
// Close the socket
|
||||||
|
this->close(); // Close the port entirely
|
||||||
|
}
|
||||||
|
|
||||||
|
void SocketComponentHelper::readTask(void* pointer) {
|
||||||
|
FW_ASSERT(pointer);
|
||||||
|
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
|
||||||
|
self->readLoop();
|
||||||
}
|
}
|
||||||
} // namespace Drv
|
} // namespace Drv
|
||||||
|
|||||||
@ -27,6 +27,12 @@ namespace Drv {
|
|||||||
*/
|
*/
|
||||||
class SocketComponentHelper {
|
class SocketComponentHelper {
|
||||||
public:
|
public:
|
||||||
|
enum OpenState{
|
||||||
|
NOT_OPEN,
|
||||||
|
OPENING,
|
||||||
|
OPEN,
|
||||||
|
SKIP
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* \brief constructs the socket read task
|
* \brief constructs the socket read task
|
||||||
*/
|
*/
|
||||||
@ -56,25 +62,6 @@ class SocketComponentHelper {
|
|||||||
const Os::Task::ParamType stack = Os::Task::TASK_DEFAULT,
|
const Os::Task::ParamType stack = Os::Task::TASK_DEFAULT,
|
||||||
const Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT);
|
const Os::Task::ParamType cpuAffinity = Os::Task::TASK_DEFAULT);
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief startup the socket for communications
|
|
||||||
*
|
|
||||||
* Status of the socket handler.
|
|
||||||
*
|
|
||||||
* Note: this just delegates to the handler
|
|
||||||
*
|
|
||||||
* \return status of open, SOCK_SUCCESS for success, something else on error
|
|
||||||
*/
|
|
||||||
SocketIpStatus startup();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Returns true when the socket is started
|
|
||||||
*
|
|
||||||
* Returns true when the socket is started up sufficiently to be actively listening to clients. Returns false
|
|
||||||
* otherwise. This means `startup()` was called and returned success.
|
|
||||||
*/
|
|
||||||
bool isStarted();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief open the socket for communications
|
* \brief open the socket for communications
|
||||||
*
|
*
|
||||||
@ -129,9 +116,7 @@ class SocketComponentHelper {
|
|||||||
/**
|
/**
|
||||||
* \brief close the socket communications
|
* \brief close the socket communications
|
||||||
*
|
*
|
||||||
* Typically stopping the socket read task will shutdown the connection. However, in cases where the read task
|
* Close the client connection. This will ensure that the resources used are cleaned-up.
|
||||||
* will not be started, this function may be used to close the socket. This calls a full `close` on the client
|
|
||||||
* socket.
|
|
||||||
*
|
*
|
||||||
* Note: this just delegates to the handler
|
* Note: this just delegates to the handler
|
||||||
*/
|
*/
|
||||||
@ -140,14 +125,18 @@ class SocketComponentHelper {
|
|||||||
/**
|
/**
|
||||||
* \brief shutdown the socket communications
|
* \brief shutdown the socket communications
|
||||||
*
|
*
|
||||||
* Typically stopping the socket read task will shutdown the connection. However, in cases where the read task
|
* Shutdown communication. This will begin the process of cleanly closing communications. This process will be
|
||||||
* will not be started, this function may be used to close the socket. This calls a full `shutdown` on the client
|
* finished with a receive of 0 size and should be followed by a close.
|
||||||
* socket.
|
|
||||||
*
|
*
|
||||||
* Note: this just delegates to the handler
|
* Note: this just delegates to the handler
|
||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief is the read loop running
|
||||||
|
*/
|
||||||
|
bool running();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief stop the socket read task and close the associated socket.
|
* \brief stop the socket read task and close the associated socket.
|
||||||
*
|
*
|
||||||
@ -166,8 +155,11 @@ class SocketComponentHelper {
|
|||||||
*/
|
*/
|
||||||
Os::Task::Status join();
|
Os::Task::Status join();
|
||||||
|
|
||||||
|
|
||||||
PROTECTED:
|
PROTECTED:
|
||||||
|
/**
|
||||||
|
* \brief receive off the TCP socket
|
||||||
|
*/
|
||||||
|
virtual void readLoop();
|
||||||
/**
|
/**
|
||||||
* \brief returns a reference to the socket handler
|
* \brief returns a reference to the socket handler
|
||||||
*
|
*
|
||||||
@ -209,6 +201,7 @@ class SocketComponentHelper {
|
|||||||
*/
|
*/
|
||||||
virtual void connected() = 0;
|
virtual void connected() = 0;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief a task designed to read from the socket and output incoming data
|
* \brief a task designed to read from the socket and output incoming data
|
||||||
*
|
*
|
||||||
@ -218,13 +211,10 @@ class SocketComponentHelper {
|
|||||||
|
|
||||||
Os::Task m_task;
|
Os::Task m_task;
|
||||||
Os::Mutex m_lock;
|
Os::Mutex m_lock;
|
||||||
NATIVE_INT_TYPE m_fd;
|
SocketDescriptor m_descriptor;
|
||||||
bool m_reconnect; //!< Force reconnection
|
bool m_reconnect = false; //!< Force reconnection
|
||||||
bool m_stop; //!< Stops the task when set to true
|
bool m_stop = true; //!< Stops the task when set to true
|
||||||
bool m_started; //!< Have we successfully started the socket
|
OpenState m_open = OpenState::NOT_OPEN; //!< Have we successfully opened
|
||||||
bool m_open; //!< Have we successfully opened
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif // DRV_SocketComponentHelper_HPP
|
#endif // DRV_SocketComponentHelper_HPP
|
||||||
|
|||||||
@ -47,7 +47,7 @@ bool TcpClientSocket::isValidPort(U16 port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SocketIpStatus TcpClientSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
SocketIpStatus TcpClientSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||||
NATIVE_INT_TYPE socketFd = -1;
|
NATIVE_INT_TYPE socketFd = -1;
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
|
|
||||||
@ -81,17 +81,17 @@ SocketIpStatus TcpClientSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
|||||||
::close(socketFd);
|
::close(socketFd);
|
||||||
return SOCK_FAILED_TO_CONNECT;
|
return SOCK_FAILED_TO_CONNECT;
|
||||||
}
|
}
|
||||||
fd = socketFd;
|
socketDescriptor.fd = socketFd;
|
||||||
Fw::Logger::log("Connected to %s:%hu as a tcp client\n", m_hostname, m_port);
|
Fw::Logger::log("Connected to %s:%hu as a tcp client\n", m_hostname, m_port);
|
||||||
return SOCK_SUCCESS;
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 TcpClientSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) {
|
I32 TcpClientSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||||
return static_cast<I32>(::send(fd, data, size, SOCKET_IP_SEND_FLAGS));
|
return static_cast<I32>(::send(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 TcpClientSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) {
|
I32 TcpClientSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||||
return static_cast<I32>(::recv(fd, data, size, SOCKET_IP_RECV_FLAGS));
|
return static_cast<I32>(::recv(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Drv
|
} // namespace Drv
|
||||||
|
|||||||
@ -44,26 +44,26 @@ class TcpClientSocket : public IpSocket {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Tcp specific implementation for opening a client socket.
|
* \brief Tcp specific implementation for opening a client socket.
|
||||||
* \param fd: (output) file descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
* \param socketDescriptor: (output) descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
||||||
* \return status of open
|
* \return status of open
|
||||||
*/
|
*/
|
||||||
SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override;
|
SocketIpStatus openProtocol(SocketDescriptor& socketDescriptor) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
||||||
* \param fd: file descriptor to send to
|
* \param socketDescriptor: descriptor to send to
|
||||||
* \param data: data to send
|
* \param data: data to send
|
||||||
* \param size: size of data to send
|
* \param size: size of data to send
|
||||||
* \return: size of data sent, or -1 on error.
|
* \return: size of data sent, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override;
|
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||||
* \param fd: file descriptor to recv from
|
* \param socketDescriptor: descriptor to recv from
|
||||||
* \param data: data pointer to fill
|
* \param data: data pointer to fill
|
||||||
* \param size: size of data buffer
|
* \param size: size of data buffer
|
||||||
* \return: size of data received, or -1 on error.
|
* \return: size of data received, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override;
|
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||||
};
|
};
|
||||||
} // namespace Drv
|
} // namespace Drv
|
||||||
|
|
||||||
|
|||||||
@ -38,15 +38,15 @@
|
|||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
TcpServerSocket::TcpServerSocket() : IpSocket(), m_base_fd(-1) {}
|
TcpServerSocket::TcpServerSocket() : IpSocket() {}
|
||||||
|
|
||||||
U16 TcpServerSocket::getListenPort() {
|
U16 TcpServerSocket::getListenPort() {
|
||||||
U16 port = this->m_port;
|
U16 port = this->m_port;
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus TcpServerSocket::startup() {
|
SocketIpStatus TcpServerSocket::startup(SocketDescriptor& socketDescriptor) {
|
||||||
NATIVE_INT_TYPE serverFd = -1;
|
PlatformIntType serverFd = -1;
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
// Acquire a socket, or return error
|
// Acquire a socket, or return error
|
||||||
if ((serverFd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
if ((serverFd = ::socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||||
@ -77,34 +77,30 @@ SocketIpStatus TcpServerSocket::startup() {
|
|||||||
::close(serverFd);
|
::close(serverFd);
|
||||||
return SOCK_FAILED_TO_READ_BACK_PORT;
|
return SOCK_FAILED_TO_READ_BACK_PORT;
|
||||||
}
|
}
|
||||||
U16 port = ntohs(address.sin_port);
|
|
||||||
Fw::Logger::log("Listening for single client at %s:%hu\n", m_hostname, port);
|
|
||||||
// TCP requires listening on the socket. Since we only expect a single client, set the TCP backlog (second argument) to 1 to prevent queuing of multiple clients.
|
// TCP requires listening on the socket. Since we only expect a single client, set the TCP backlog (second argument) to 1 to prevent queuing of multiple clients.
|
||||||
if (::listen(serverFd, 1) < 0) {
|
if (::listen(serverFd, 1) < 0) {
|
||||||
::close(serverFd);
|
::close(serverFd);
|
||||||
return SOCK_FAILED_TO_LISTEN; // What we have here is a failure to communicate
|
return SOCK_FAILED_TO_LISTEN; // What we have here is a failure to communicate
|
||||||
}
|
}
|
||||||
|
Fw::Logger::log("Listening for single client at %s:%hu\n", m_hostname, m_port);
|
||||||
m_base_fd = serverFd;
|
FW_ASSERT(serverFd != -1);
|
||||||
m_port = port;
|
socketDescriptor.serverFd = serverFd;
|
||||||
|
this->m_port = ntohs(address.sin_port);
|
||||||
return this->IpSocket::startup();
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpServerSocket::shutdown(NATIVE_INT_TYPE fd) {
|
void TcpServerSocket::terminate(const SocketDescriptor& socketDescriptor) {
|
||||||
if (this->m_base_fd != -1) {
|
(void)::close(socketDescriptor.serverFd);
|
||||||
(void)::shutdown(this->m_base_fd, SHUT_RDWR);
|
}
|
||||||
(void)::close(this->m_base_fd);
|
|
||||||
this->m_base_fd = -1;
|
SocketIpStatus TcpServerSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||||
|
PlatformIntType clientFd = -1;
|
||||||
|
PlatformIntType serverFd = socketDescriptor.serverFd;
|
||||||
|
|
||||||
|
// Check for not started yet, may be true in the case of start-up reconnect attempts
|
||||||
|
if (serverFd == -1) {
|
||||||
|
return SOCK_NOT_STARTED;
|
||||||
}
|
}
|
||||||
this->IpSocket::shutdown(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
SocketIpStatus TcpServerSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
|
||||||
NATIVE_INT_TYPE clientFd = -1;
|
|
||||||
NATIVE_INT_TYPE serverFd = -1;
|
|
||||||
|
|
||||||
serverFd = this->m_base_fd;
|
|
||||||
|
|
||||||
// TCP requires accepting on the socket to get the client socket file descriptor.
|
// TCP requires accepting on the socket to get the client socket file descriptor.
|
||||||
clientFd = ::accept(serverFd, nullptr, nullptr);
|
clientFd = ::accept(serverFd, nullptr, nullptr);
|
||||||
@ -118,18 +114,18 @@ SocketIpStatus TcpServerSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Fw::Logger::log("Accepted client at %s:%hu\n", m_hostname, m_port);
|
Fw::Logger::log("Accepted client at %s:%hu\n", m_hostname, m_port);
|
||||||
fd = clientFd;
|
socketDescriptor.fd = clientFd;
|
||||||
return SOCK_SUCCESS;
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 TcpServerSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) {
|
I32 TcpServerSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||||
return static_cast<I32>(::send(fd, data, size, SOCKET_IP_SEND_FLAGS));
|
return static_cast<I32>(::send(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS));
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 TcpServerSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) {
|
I32 TcpServerSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||||
I32 size_buf;
|
I32 size_buf;
|
||||||
// recv will return 0 if the client has done an orderly shutdown
|
// recv will return 0 if the client has done an orderly shutdown
|
||||||
size_buf = static_cast<I32>(::recv(fd, data, size, SOCKET_IP_RECV_FLAGS));
|
size_buf = static_cast<I32>(::recv(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS));
|
||||||
return size_buf;
|
return size_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
#include <IpCfg.hpp>
|
#include <IpCfg.hpp>
|
||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Helper for setting up Tcp using Berkeley sockets as a server
|
* \brief Helper for setting up Tcp using Berkeley sockets as a server
|
||||||
*
|
*
|
||||||
@ -36,18 +37,20 @@ class TcpServerSocket : public IpSocket {
|
|||||||
* Opens the server's listening socket such that this server can listen for incoming client requests. Given the
|
* Opens the server's listening socket such that this server can listen for incoming client requests. Given the
|
||||||
* nature of this component, only one (1) client can be handled at a time. After this call succeeds, clients may
|
* nature of this component, only one (1) client can be handled at a time. After this call succeeds, clients may
|
||||||
* connect. This call does not block, block occurs on `open` while waiting to accept incoming clients.
|
* connect. This call does not block, block occurs on `open` while waiting to accept incoming clients.
|
||||||
|
* \param socketDescriptor: server descriptor will be written here
|
||||||
* \return status of the server socket setup.
|
* \return status of the server socket setup.
|
||||||
*/
|
*/
|
||||||
SocketIpStatus startup() override;
|
SocketIpStatus startup(SocketDescriptor& socketDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Shutdown and close the server socket followed by the open client
|
* \brief close the server socket created by the `startup` call
|
||||||
*
|
*
|
||||||
* \param fd: file descriptor to shutdown
|
* Calls the close function on the server socket. No shutdown is performed on the server socket, as that is left to
|
||||||
* First, this calls `shutdown` and `close` on the server socket and then calls the close method to `shutdown` and
|
* the individual client sockets.
|
||||||
* `close` the client.
|
*
|
||||||
|
* \param socketDescriptor: descriptor to close
|
||||||
*/
|
*/
|
||||||
void shutdown(NATIVE_INT_TYPE fd) override;
|
void terminate(const SocketDescriptor& socketDescriptor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief get the port being listened on
|
* \brief get the port being listened on
|
||||||
@ -62,28 +65,29 @@ class TcpServerSocket : public IpSocket {
|
|||||||
PROTECTED:
|
PROTECTED:
|
||||||
/**
|
/**
|
||||||
* \brief Tcp specific implementation for opening a client socket connected to this server.
|
* \brief Tcp specific implementation for opening a client socket connected to this server.
|
||||||
* \param fd: (output) file descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
* \param socketDescriptor: (output) descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
||||||
* \return status of open
|
* \return status of open
|
||||||
*/
|
*/
|
||||||
SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override;
|
SocketIpStatus openProtocol(SocketDescriptor& socketDescriptor) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
||||||
* \param fd: file descriptor to send to
|
* \param socketDescriptor: descriptor to send to
|
||||||
* \param data: data to send
|
* \param data: data to send
|
||||||
* \param size: size of data to send
|
* \param size: size of data to send
|
||||||
* \return: size of data sent, or -1 on error.
|
* \return: size of data sent, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override;
|
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||||
* \param fd: file descriptor to recv from
|
* \param socketDescriptor: descriptor to recv from
|
||||||
* \param data: data pointer to fill
|
* \param data: data pointer to fill
|
||||||
* \param size: size of data buffer
|
* \param size: size of data buffer
|
||||||
* \return: size of data received, or -1 on error.
|
* \return: size of data received, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override;
|
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||||
PRIVATE:
|
|
||||||
NATIVE_INT_TYPE m_base_fd; //!< File descriptor of the listening socket
|
|
||||||
|
|
||||||
};
|
};
|
||||||
} // namespace Drv
|
} // namespace Drv
|
||||||
|
|
||||||
|
|||||||
@ -80,9 +80,9 @@ U16 UdpSocket::getRecvPort() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SocketIpStatus UdpSocket::bind(NATIVE_INT_TYPE fd) {
|
SocketIpStatus UdpSocket::bind(const PlatformIntType fd) {
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
FW_ASSERT(-1 != fd);
|
FW_ASSERT(fd != -1);
|
||||||
|
|
||||||
// Set up the address port and name
|
// Set up the address port and name
|
||||||
address.sin_family = AF_INET;
|
address.sin_family = AF_INET;
|
||||||
@ -112,7 +112,7 @@ SocketIpStatus UdpSocket::bind(NATIVE_INT_TYPE fd) {
|
|||||||
return SOCK_SUCCESS;
|
return SOCK_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
SocketIpStatus UdpSocket::openProtocol(SocketDescriptor& socketDescriptor) {
|
||||||
SocketIpStatus status = SOCK_SUCCESS;
|
SocketIpStatus status = SOCK_SUCCESS;
|
||||||
NATIVE_INT_TYPE socketFd = -1;
|
NATIVE_INT_TYPE socketFd = -1;
|
||||||
struct sockaddr_in address;
|
struct sockaddr_in address;
|
||||||
@ -169,19 +169,19 @@ SocketIpStatus UdpSocket::openProtocol(NATIVE_INT_TYPE& fd) {
|
|||||||
port);
|
port);
|
||||||
}
|
}
|
||||||
FW_ASSERT(status == SOCK_SUCCESS, status);
|
FW_ASSERT(status == SOCK_SUCCESS, status);
|
||||||
fd = socketFd;
|
socketDescriptor.fd = socketFd;
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 UdpSocket::sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) {
|
I32 UdpSocket::sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
|
||||||
FW_ASSERT(this->m_state->m_addr_send.sin_family != 0); // Make sure the address was previously setup
|
FW_ASSERT(this->m_state->m_addr_send.sin_family != 0); // Make sure the address was previously setup
|
||||||
return static_cast<I32>(::sendto(fd, data, size, SOCKET_IP_SEND_FLAGS,
|
return static_cast<I32>(::sendto(socketDescriptor.fd, data, size, SOCKET_IP_SEND_FLAGS,
|
||||||
reinterpret_cast<struct sockaddr *>(&this->m_state->m_addr_send), sizeof(this->m_state->m_addr_send)));
|
reinterpret_cast<struct sockaddr *>(&this->m_state->m_addr_send), sizeof(this->m_state->m_addr_send)));
|
||||||
}
|
}
|
||||||
|
|
||||||
I32 UdpSocket::recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) {
|
I32 UdpSocket::recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) {
|
||||||
FW_ASSERT(this->m_state->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
|
FW_ASSERT(this->m_state->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
|
||||||
return static_cast<I32>(::recvfrom(fd, data, size, SOCKET_IP_RECV_FLAGS, nullptr, nullptr));
|
return static_cast<I32>(::recvfrom(socketDescriptor.fd, data, size, SOCKET_IP_RECV_FLAGS, nullptr, nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Drv
|
} // namespace Drv
|
||||||
|
|||||||
@ -18,9 +18,6 @@
|
|||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief a structure used to hold the encapsulated socket state to prevent namespace collision
|
|
||||||
*/
|
|
||||||
struct SocketState;
|
struct SocketState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,32 +86,32 @@ class UdpSocket : public IpSocket {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief bind the UDP to a port such that it can receive packets at the previously configured port
|
* \brief bind the UDP to a port such that it can receive packets at the previously configured port
|
||||||
* \param fd: socket file descriptor used in bind
|
* \param socketDescriptor: socket descriptor used in bind
|
||||||
* \return status of the bind
|
* \return status of the bind
|
||||||
*/
|
*/
|
||||||
SocketIpStatus bind(NATIVE_INT_TYPE fd);
|
SocketIpStatus bind(const PlatformIntType fd);
|
||||||
/**
|
/**
|
||||||
* \brief udp specific implementation for opening a socket.
|
* \brief udp specific implementation for opening a socket.
|
||||||
* \param fd: (output) file descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
* \param socketDescriptor: (output) file descriptor opened. Only valid on SOCK_SUCCESS. Otherwise will be invalid
|
||||||
* \return status of open
|
* \return status of open
|
||||||
*/
|
*/
|
||||||
SocketIpStatus openProtocol(NATIVE_INT_TYPE& fd) override;
|
SocketIpStatus openProtocol(SocketDescriptor& socketDescriptor) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
* \brief Protocol specific implementation of send. Called directly with retry from send.
|
||||||
* \param fd: file descriptor to send to
|
* \param socketDescriptor: descriptor to send to
|
||||||
* \param data: data to send
|
* \param data: data to send
|
||||||
* \param size: size of data to send
|
* \param size: size of data to send
|
||||||
* \return: size of data sent, or -1 on error.
|
* \return: size of data sent, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 sendProtocol(NATIVE_INT_TYPE fd, const U8* const data, const U32 size) override;
|
I32 sendProtocol(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) override;
|
||||||
/**
|
/**
|
||||||
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
* \brief Protocol specific implementation of recv. Called directly with error handling from recv.
|
||||||
* \param fd: file descriptor to recv from
|
* \param socketDescriptor: descriptor to recv from
|
||||||
* \param data: data pointer to fill
|
* \param data: data pointer to fill
|
||||||
* \param size: size of data buffer
|
* \param size: size of data buffer
|
||||||
* \return: size of data received, or -1 on error.
|
* \return: size of data received, or -1 on error.
|
||||||
*/
|
*/
|
||||||
I32 recvProtocol(NATIVE_INT_TYPE fd, U8* const data, const U32 size) override;
|
I32 recvProtocol(const SocketDescriptor& socketDescriptor, U8* const data, const U32 size) override;
|
||||||
private:
|
private:
|
||||||
SocketState* m_state; //!< State storage
|
SocketState* m_state; //!< State storage
|
||||||
U16 m_recv_port; //!< IP address port used
|
U16 m_recv_port; //!< IP address port used
|
||||||
|
|||||||
@ -43,21 +43,46 @@ void validate_random_buffer(Fw::Buffer &buffer, U8 *data) {
|
|||||||
buffer.setSize(0);
|
buffer.setSize(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_random_buffer(Fw::Buffer &buffer) {
|
U32 fill_random_buffer(Fw::Buffer &buffer) {
|
||||||
buffer.setSize(STest::Pick::lowerUpper(1, buffer.getSize()));
|
buffer.setSize(STest::Pick::lowerUpper(1, buffer.getSize()));
|
||||||
fill_random_data(buffer.getData(), buffer.getSize());
|
fill_random_data(buffer.getData(), buffer.getSize());
|
||||||
|
return static_cast<U32>(buffer.getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, NATIVE_INT_TYPE sender_fd, NATIVE_INT_TYPE receiver_fd) {
|
void drain(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd) {
|
||||||
|
Drv::SocketIpStatus status = SOCK_SUCCESS;
|
||||||
|
// Drain the server in preparation for close
|
||||||
|
while (status == Drv::SOCK_SUCCESS || status == Drv::SOCK_NO_DATA_AVAILABLE) {
|
||||||
|
U8 buffer[1];
|
||||||
|
U32 size = sizeof buffer;
|
||||||
|
status = receiver.recv(receiver_fd, buffer, size);
|
||||||
|
}
|
||||||
|
ASSERT_EQ(status, Drv::SocketIpStatus::SOCK_DISCONNECTED) << "Socket did not disconnect as expected";
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, U32 size) {
|
||||||
|
ASSERT_NE(buffer, nullptr);
|
||||||
|
U32 received_size = 0;
|
||||||
|
Drv::SocketIpStatus status;
|
||||||
|
do {
|
||||||
|
U32 size_in_out = size - received_size;
|
||||||
|
status = receiver.recv(receiver_fd, buffer + received_size, size_in_out);
|
||||||
|
ASSERT_TRUE((status == Drv::SOCK_NO_DATA_AVAILABLE || status == Drv::SOCK_SUCCESS));
|
||||||
|
received_size += size_in_out;
|
||||||
|
} while (size > received_size);
|
||||||
|
EXPECT_EQ(received_size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, Drv::SocketDescriptor& sender_fd, Drv::SocketDescriptor& receiver_fd) {
|
||||||
U32 size = MAX_DRV_TEST_MESSAGE_SIZE;
|
U32 size = MAX_DRV_TEST_MESSAGE_SIZE;
|
||||||
|
|
||||||
U8 buffer_out[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
U8 buffer_out[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
||||||
U8 buffer_in[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
U8 buffer_in[MAX_DRV_TEST_MESSAGE_SIZE] = {0};
|
||||||
|
|
||||||
// Send receive validate block
|
// Send receive validate block
|
||||||
Drv::Test::fill_random_data(buffer_out, MAX_DRV_TEST_MESSAGE_SIZE);
|
Drv::Test::fill_random_data(buffer_out, MAX_DRV_TEST_MESSAGE_SIZE);
|
||||||
EXPECT_EQ(sender.send(sender_fd, buffer_out, MAX_DRV_TEST_MESSAGE_SIZE), Drv::SOCK_SUCCESS);
|
EXPECT_EQ(sender.send(sender_fd, buffer_out, MAX_DRV_TEST_MESSAGE_SIZE), Drv::SOCK_SUCCESS);
|
||||||
EXPECT_EQ(receiver.recv(receiver_fd, buffer_in, size), Drv::SOCK_SUCCESS);
|
receive_all(receiver, receiver_fd, buffer_in, size);
|
||||||
EXPECT_EQ(size, static_cast<U32>(MAX_DRV_TEST_MESSAGE_SIZE));
|
|
||||||
Drv::Test::validate_random_data(buffer_out, buffer_in, MAX_DRV_TEST_MESSAGE_SIZE);
|
Drv::Test::validate_random_data(buffer_out, buffer_in, MAX_DRV_TEST_MESSAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ void validate_random_buffer(Fw::Buffer &buffer, U8 *data);
|
|||||||
* Fill random data into the buffer (using a random length).
|
* Fill random data into the buffer (using a random length).
|
||||||
* @param buffer: buffer to fill.
|
* @param buffer: buffer to fill.
|
||||||
*/
|
*/
|
||||||
void fill_random_buffer(Fw::Buffer &buffer);
|
U32 fill_random_buffer(Fw::Buffer &buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send/receive pair.
|
* Send/receive pair.
|
||||||
@ -56,7 +56,23 @@ void fill_random_buffer(Fw::Buffer &buffer);
|
|||||||
* @param sender_fd: file descriptor for sender
|
* @param sender_fd: file descriptor for sender
|
||||||
* @param receiver_fd: file descriptor for receiver
|
* @param receiver_fd: file descriptor for receiver
|
||||||
*/
|
*/
|
||||||
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, NATIVE_INT_TYPE sender_fd, NATIVE_INT_TYPE receiver_fd);
|
void send_recv(Drv::IpSocket& sender, Drv::IpSocket& receiver, Drv::SocketDescriptor& sender_fd, Drv::SocketDescriptor& receiver_fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drain bytes from the socket until disconnect received.
|
||||||
|
* @warning: must have called shutdown on the remote before calling this
|
||||||
|
* @param drain_fd: file descriptor for draining
|
||||||
|
*/
|
||||||
|
void drain(Drv::IpSocket& receiver, Drv::SocketDescriptor& drain_fd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive all data, reassembling the frame
|
||||||
|
* @param receiver: receiver
|
||||||
|
* @param receiver_fd: receiver descriptor
|
||||||
|
* @param buffer: buffer
|
||||||
|
* @param size: size to receive
|
||||||
|
*/
|
||||||
|
void receive_all(Drv::IpSocket& receiver, Drv::SocketDescriptor& receiver_fd, U8* buffer, U32 size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait on socket change.
|
* Wait on socket change.
|
||||||
|
|||||||
@ -19,11 +19,11 @@ void test_with_loop(U32 iterations) {
|
|||||||
|
|
||||||
U16 port = 0; // Choose a port
|
U16 port = 0; // Choose a port
|
||||||
Drv::TcpServerSocket server;
|
Drv::TcpServerSocket server;
|
||||||
NATIVE_INT_TYPE server_fd = -1;
|
Drv::SocketDescriptor server_fd;
|
||||||
NATIVE_INT_TYPE client_fd = -1;
|
Drv::SocketDescriptor client_fd;
|
||||||
server.configure("127.0.0.1", port, 0, 100);
|
server.configure("127.0.0.1", port, 0, 100);
|
||||||
EXPECT_EQ(server.startup(), Drv::SOCK_SUCCESS);
|
EXPECT_EQ(server.startup(server_fd), Drv::SOCK_SUCCESS);
|
||||||
Drv::Test::force_recv_timeout(server_fd, server);
|
Drv::Test::force_recv_timeout(server_fd.fd, server);
|
||||||
|
|
||||||
// Loop through a bunch of client disconnects
|
// Loop through a bunch of client disconnects
|
||||||
for (U32 i = 0; i < iterations; i++) {
|
for (U32 i = 0; i < iterations; i++) {
|
||||||
@ -31,7 +31,7 @@ void test_with_loop(U32 iterations) {
|
|||||||
client.configure("127.0.0.1", server.getListenPort(),0,100);
|
client.configure("127.0.0.1", server.getListenPort(),0,100);
|
||||||
// client_fd gets assigned a real value here
|
// client_fd gets assigned a real value here
|
||||||
status1 = client.open(client_fd);
|
status1 = client.open(client_fd);
|
||||||
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status1, Drv::SOCK_SUCCESS) << "With errno: " << errno;
|
||||||
|
|
||||||
// client_fd gets assigned a real value here
|
// client_fd gets assigned a real value here
|
||||||
status2 = server.open(server_fd);
|
status2 = server.open(server_fd);
|
||||||
@ -41,15 +41,18 @@ void test_with_loop(U32 iterations) {
|
|||||||
// If all the opens worked, then run this
|
// If all the opens worked, then run this
|
||||||
if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) {
|
if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) {
|
||||||
// Force the sockets not to hang, if at all possible
|
// Force the sockets not to hang, if at all possible
|
||||||
Drv::Test::force_recv_timeout(client_fd, client);
|
Drv::Test::force_recv_timeout(client_fd.fd, client);
|
||||||
Drv::Test::force_recv_timeout(server_fd, server);
|
Drv::Test::force_recv_timeout(server_fd.fd, server);
|
||||||
Drv::Test::send_recv(server, client, server_fd, client_fd);
|
Drv::Test::send_recv(server, client, server_fd, client_fd);
|
||||||
Drv::Test::send_recv(client, server, client_fd, server_fd);
|
Drv::Test::send_recv(client, server, client_fd, server_fd);
|
||||||
}
|
}
|
||||||
client.close(client_fd);
|
server.shutdown(client_fd);
|
||||||
|
// Drain the server before close
|
||||||
|
Drv::Test::drain(server, server_fd);
|
||||||
server.close(server_fd);
|
server.close(server_fd);
|
||||||
|
client.close(client_fd);
|
||||||
}
|
}
|
||||||
server.shutdown(server_fd);
|
server.terminate(server_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,8 +15,8 @@ void test_with_loop(U32 iterations, bool duplex) {
|
|||||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||||
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
||||||
|
|
||||||
NATIVE_INT_TYPE udp1_fd = -1;
|
Drv::SocketDescriptor udp1_fd;
|
||||||
NATIVE_INT_TYPE udp2_fd = -1;
|
Drv::SocketDescriptor udp2_fd;
|
||||||
|
|
||||||
U16 port1 = Drv::Test::get_free_port(true);
|
U16 port1 = Drv::Test::get_free_port(true);
|
||||||
ASSERT_NE(0, port1);
|
ASSERT_NE(0, port1);
|
||||||
@ -49,8 +49,8 @@ void test_with_loop(U32 iterations, bool duplex) {
|
|||||||
// If all the opens worked, then run this
|
// If all the opens worked, then run this
|
||||||
if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) {
|
if (Drv::SOCK_SUCCESS == status1 && Drv::SOCK_SUCCESS == status2) {
|
||||||
// Force the sockets not to hang, if at all possible
|
// Force the sockets not to hang, if at all possible
|
||||||
Drv::Test::force_recv_timeout(udp1_fd, udp1);
|
Drv::Test::force_recv_timeout(udp1_fd.fd, udp1);
|
||||||
Drv::Test::force_recv_timeout(udp2_fd, udp2);
|
Drv::Test::force_recv_timeout(udp2_fd.fd, udp2);
|
||||||
Drv::Test::send_recv(udp1, udp2, udp1_fd, udp2_fd);
|
Drv::Test::send_recv(udp1, udp2, udp1_fd, udp2_fd);
|
||||||
// Allow duplex connections
|
// Allow duplex connections
|
||||||
if (duplex) {
|
if (duplex) {
|
||||||
|
|||||||
@ -23,8 +23,7 @@ namespace Drv {
|
|||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
TcpClientComponentImpl::TcpClientComponentImpl(const char* const compName)
|
TcpClientComponentImpl::TcpClientComponentImpl(const char* const compName)
|
||||||
: TcpClientComponentBase(compName),
|
: TcpClientComponentBase(compName) {}
|
||||||
SocketComponentHelper() {}
|
|
||||||
|
|
||||||
SocketIpStatus TcpClientComponentImpl::configure(const char* hostname,
|
SocketIpStatus TcpClientComponentImpl::configure(const char* hostname,
|
||||||
const U16 port,
|
const U16 port,
|
||||||
@ -35,7 +34,6 @@ SocketIpStatus TcpClientComponentImpl::configure(const char* hostname,
|
|||||||
// Check that ensures the configured buffer size fits within the limits fixed-width type, U32
|
// Check that ensures the configured buffer size fits within the limits fixed-width type, U32
|
||||||
FW_ASSERT(buffer_size <= std::numeric_limits<U32>::max(), static_cast<FwAssertArgType>(buffer_size));
|
FW_ASSERT(buffer_size <= std::numeric_limits<U32>::max(), static_cast<FwAssertArgType>(buffer_size));
|
||||||
m_allocation_size = buffer_size; // Store the buffer size
|
m_allocation_size = buffer_size; // Store the buffer size
|
||||||
(void)startup();
|
|
||||||
return m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds);
|
return m_socket.configure(hostname, port, send_timeout_seconds, send_timeout_microseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,22 +5,22 @@
|
|||||||
#include "TcpClientTester.hpp"
|
#include "TcpClientTester.hpp"
|
||||||
|
|
||||||
|
|
||||||
TEST(Nominal, BasicMessaging) {
|
TEST(Nominal, TcpClientBasicMessaging) {
|
||||||
Drv::TcpClientTester tester;
|
Drv::TcpClientTester tester;
|
||||||
tester.test_basic_messaging();
|
tester.test_basic_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Nominal, BasicReceiveThread) {
|
TEST(Nominal, TcpClientBasicReceiveThread) {
|
||||||
Drv::TcpClientTester tester;
|
Drv::TcpClientTester tester;
|
||||||
tester.test_receive_thread();
|
tester.test_receive_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, MultiMessaging) {
|
TEST(Reconnect, TcpClientMultiMessaging) {
|
||||||
Drv::TcpClientTester tester;
|
Drv::TcpClientTester tester;
|
||||||
tester.test_multiple_messaging();
|
tester.test_multiple_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, ReceiveThreadReconnect) {
|
TEST(Reconnect, TcpClientReceiveThreadReconnect) {
|
||||||
Drv::TcpClientTester tester;
|
Drv::TcpClientTester tester;
|
||||||
tester.test_advanced_reconnect();
|
tester.test_advanced_reconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,9 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
|
|
||||||
Drv::TcpServerSocket server;
|
Drv::TcpServerSocket server;
|
||||||
server.configure("127.0.0.1", port, 0, 100);
|
server.configure("127.0.0.1", port, 0, 100);
|
||||||
NATIVE_INT_TYPE client_fd = -1;
|
Drv::SocketDescriptor server_fd;
|
||||||
|
|
||||||
serverStat = server.startup();
|
serverStat = server.startup(server_fd);
|
||||||
this->component.configure("127.0.0.1", server.getListenPort(), 0, 100);
|
this->component.configure("127.0.0.1", server.getListenPort(), 0, 100);
|
||||||
|
|
||||||
ASSERT_EQ(serverStat, SOCK_SUCCESS)
|
ASSERT_EQ(serverStat, SOCK_SUCCESS)
|
||||||
@ -60,36 +60,28 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
}
|
}
|
||||||
EXPECT_TRUE(this->component.isOpened());
|
EXPECT_TRUE(this->component.isOpened());
|
||||||
// fd has now been updated to be a value we need to keep track of
|
// fd has now been updated to be a value we need to keep track of
|
||||||
status2 = server.open(client_fd);
|
status2 = server.open(server_fd);
|
||||||
|
|
||||||
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
||||||
|
|
||||||
status2 = Drv::SOCK_NO_DATA_AVAILABLE;
|
|
||||||
|
|
||||||
// If all the opens worked, then run this
|
// If all the opens worked, then run this
|
||||||
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
||||||
(this->component.isOpened())) {
|
(this->component.isOpened())) {
|
||||||
// Force the sockets not to hang, if at all possible
|
// Force the sockets not to hang, if at all possible
|
||||||
Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler());
|
Drv::Test::force_recv_timeout(this->component.m_descriptor.fd, this->component.getSocketHandler());
|
||||||
Drv::Test::force_recv_timeout(server.m_base_fd, server);
|
Drv::Test::force_recv_timeout(server_fd.serverFd, server);
|
||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
||||||
EXPECT_EQ(status, SendStatus::SEND_OK);
|
EXPECT_EQ(status, SendStatus::SEND_OK);
|
||||||
U16 counter = 0;
|
Drv::Test::receive_all(server, server_fd, buffer, size);
|
||||||
while ((status2 == Drv::SOCK_NO_DATA_AVAILABLE) and counter < Drv::Test::MAX_ITER) {
|
|
||||||
status2 = server.recv(client_fd, buffer, size);
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
|
||||||
EXPECT_EQ(size, m_data_buffer.getSize());
|
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||||
// If receive thread is live, try the other way
|
// If receive thread is live, try the other way
|
||||||
if (recv_thread) {
|
if (recv_thread) {
|
||||||
m_spinner = false;
|
m_spinner = false;
|
||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
status2 = server.send(client_fd, m_data_buffer.getData(), m_data_buffer.getSize());
|
status2 = server.send(server_fd, m_data_buffer.getData(), m_data_buffer.getSize());
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
||||||
from_deallocate_handler(0, m_data_buffer);
|
from_deallocate_handler(0, m_data_buffer);
|
||||||
while (not m_spinner) {}
|
while (not m_spinner) {}
|
||||||
@ -100,11 +92,16 @@ void TcpClientTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
this->component.stop();
|
this->component.stop();
|
||||||
this->component.join();
|
this->component.join();
|
||||||
} else {
|
} else {
|
||||||
|
// Client should close to initiate a clean shutdown
|
||||||
|
// This is because the server "can't know" if the client is done until
|
||||||
|
// this close is hit, or the server initiates the shutdown.
|
||||||
this->component.close();
|
this->component.close();
|
||||||
}
|
}
|
||||||
server.close(client_fd);
|
// Safe server shutdown after client
|
||||||
|
Drv::Test::drain(server, server_fd);
|
||||||
|
server.close(server_fd);
|
||||||
}
|
}
|
||||||
server.shutdown(client_fd);
|
server.terminate(server_fd);
|
||||||
ASSERT_from_ready_SIZE(iterations);
|
ASSERT_from_ready_SIZE(iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include <Drv/TcpServer/TcpServerComponentImpl.hpp>
|
#include <Drv/TcpServer/TcpServerComponentImpl.hpp>
|
||||||
#include <FpConfig.hpp>
|
#include <FpConfig.hpp>
|
||||||
#include "Fw/Types/Assert.hpp"
|
#include "Fw/Types/Assert.hpp"
|
||||||
|
#include "Fw/Logger/Logger.hpp"
|
||||||
|
|
||||||
namespace Drv {
|
namespace Drv {
|
||||||
|
|
||||||
@ -21,8 +22,7 @@ namespace Drv {
|
|||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
TcpServerComponentImpl::TcpServerComponentImpl(const char* const compName)
|
TcpServerComponentImpl::TcpServerComponentImpl(const char* const compName)
|
||||||
: TcpServerComponentBase(compName),
|
: TcpServerComponentBase(compName) {}
|
||||||
SocketComponentHelper() {}
|
|
||||||
|
|
||||||
SocketIpStatus TcpServerComponentImpl::configure(const char* hostname,
|
SocketIpStatus TcpServerComponentImpl::configure(const char* hostname,
|
||||||
const U16 port,
|
const U16 port,
|
||||||
@ -70,6 +70,48 @@ void TcpServerComponentImpl::connected() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TcpServerComponentImpl::isStarted() {
|
||||||
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
|
return this->m_descriptor.serverFd != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SocketIpStatus TcpServerComponentImpl::startup() {
|
||||||
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
|
Drv::SocketIpStatus status = SOCK_SUCCESS;
|
||||||
|
// Prevent multiple startup attempts
|
||||||
|
if (this->m_descriptor.serverFd == -1) {
|
||||||
|
status = this->m_socket.startup(this->m_descriptor);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpServerComponentImpl::terminate() {
|
||||||
|
Os::ScopeLock scopedLock(this->m_lock);
|
||||||
|
this->m_socket.terminate(this->m_descriptor);
|
||||||
|
this->m_descriptor.serverFd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpServerComponentImpl::readLoop() {
|
||||||
|
Drv::SocketIpStatus status = Drv::SocketIpStatus::SOCK_NOT_STARTED;
|
||||||
|
// Keep trying to reconnect until the status is good, told to stop, or reconnection is turned off
|
||||||
|
do {
|
||||||
|
status = this->startup();
|
||||||
|
if (status != SOCK_SUCCESS) {
|
||||||
|
Fw::Logger::log("[WARNING] Failed to listen on port %hu with status %d\n", this->getListenPort(), status);
|
||||||
|
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (this->running() && status != SOCK_SUCCESS && this->m_reconnect);
|
||||||
|
// If start up was successful then perform normal operations
|
||||||
|
if (this->running() && status == SOCK_SUCCESS) {
|
||||||
|
// Perform the nominal read loop
|
||||||
|
SocketComponentHelper::readLoop();
|
||||||
|
// Terminate the server
|
||||||
|
this->terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Handler implementations for user-defined typed input ports
|
// Handler implementations for user-defined typed input ports
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|||||||
@ -62,6 +62,26 @@ class TcpServerComponentImpl : public TcpServerComponentBase, public SocketCompo
|
|||||||
const U32 send_timeout_seconds = SOCKET_SEND_TIMEOUT_SECONDS,
|
const U32 send_timeout_seconds = SOCKET_SEND_TIMEOUT_SECONDS,
|
||||||
const U32 send_timeout_microseconds = SOCKET_SEND_TIMEOUT_MICROSECONDS);
|
const U32 send_timeout_microseconds = SOCKET_SEND_TIMEOUT_MICROSECONDS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief is started
|
||||||
|
*/
|
||||||
|
bool isStarted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief startup the server socket for communications
|
||||||
|
*
|
||||||
|
* Start up the server socket by listening on a port. Note: does not accept clients, this is done in open to
|
||||||
|
* facilitate re-connection of clients.
|
||||||
|
*/
|
||||||
|
SocketIpStatus startup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief terminate the server socket
|
||||||
|
*
|
||||||
|
* Close the server socket. Should be done after all clients are shutdown and closed.
|
||||||
|
*/
|
||||||
|
void terminate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief get the port being listened on
|
* \brief get the port being listened on
|
||||||
*
|
*
|
||||||
@ -85,7 +105,7 @@ class TcpServerComponentImpl : public TcpServerComponentBase, public SocketCompo
|
|||||||
*
|
*
|
||||||
* \return IpSocket reference
|
* \return IpSocket reference
|
||||||
*/
|
*/
|
||||||
IpSocket& getSocketHandler();
|
IpSocket& getSocketHandler() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief returns a buffer to fill with data
|
* \brief returns a buffer to fill with data
|
||||||
@ -95,7 +115,7 @@ class TcpServerComponentImpl : public TcpServerComponentBase, public SocketCompo
|
|||||||
*
|
*
|
||||||
* \return Fw::Buffer to fill with data
|
* \return Fw::Buffer to fill with data
|
||||||
*/
|
*/
|
||||||
Fw::Buffer getBuffer();
|
Fw::Buffer getBuffer() override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief sends a buffer to be filled with data
|
* \brief sends a buffer to be filled with data
|
||||||
@ -105,13 +125,17 @@ class TcpServerComponentImpl : public TcpServerComponentBase, public SocketCompo
|
|||||||
*
|
*
|
||||||
* \return Fw::Buffer filled with data to send out
|
* \return Fw::Buffer filled with data to send out
|
||||||
*/
|
*/
|
||||||
void sendBuffer(Fw::Buffer buffer, SocketIpStatus status);
|
void sendBuffer(Fw::Buffer buffer, SocketIpStatus status) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief called when the IPv4 system has been connected
|
* \brief called when the IPv4 system has been connected
|
||||||
*/
|
*/
|
||||||
void connected();
|
void connected() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief read from the socket, overridden to start and terminate the server socket
|
||||||
|
*/
|
||||||
|
void readLoop() override;
|
||||||
|
|
||||||
PRIVATE:
|
PRIVATE:
|
||||||
|
|
||||||
@ -134,7 +158,7 @@ class TcpServerComponentImpl : public TcpServerComponentBase, public SocketCompo
|
|||||||
* \param fwBuffer: buffer containing data to be sent
|
* \param fwBuffer: buffer containing data to be sent
|
||||||
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
|
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
|
||||||
*/
|
*/
|
||||||
Drv::SendStatus send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer);
|
Drv::SendStatus send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer) override;
|
||||||
|
|
||||||
Drv::TcpServerSocket m_socket; //!< Socket implementation
|
Drv::TcpServerSocket m_socket; //!< Socket implementation
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,22 +4,22 @@
|
|||||||
|
|
||||||
#include "TcpServerTester.hpp"
|
#include "TcpServerTester.hpp"
|
||||||
|
|
||||||
TEST(Nominal, BasicMessaging) {
|
TEST(Nominal, TcpServerBasicMessaging) {
|
||||||
Drv::TcpServerTester tester;
|
Drv::TcpServerTester tester;
|
||||||
tester.test_basic_messaging();
|
tester.test_basic_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Nominal, BasicReceiveThread) {
|
TEST(Nominal, TcpServerBasicReceiveThread) {
|
||||||
Drv::TcpServerTester tester;
|
Drv::TcpServerTester tester;
|
||||||
tester.test_receive_thread();
|
tester.test_receive_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, MultiMessaging) {
|
TEST(Reconnect, TcpServerMultiMessaging) {
|
||||||
Drv::TcpServerTester tester;
|
Drv::TcpServerTester tester;
|
||||||
tester.test_multiple_messaging();
|
tester.test_multiple_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, ReceiveThreadReconnect) {
|
TEST(Reconnect, TcpServerReceiveThreadReconnect) {
|
||||||
Drv::TcpServerTester tester;
|
Drv::TcpServerTester tester;
|
||||||
tester.test_advanced_reconnect();
|
tester.test_advanced_reconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,10 +27,9 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
U8 buffer[sizeof(m_data_storage)] = {};
|
U8 buffer[sizeof(m_data_storage)] = {};
|
||||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||||
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
||||||
Drv::SocketIpStatus serverStat = Drv::SOCK_SUCCESS;
|
|
||||||
|
|
||||||
U16 port = 0;
|
U16 port = 0;
|
||||||
NATIVE_INT_TYPE client_fd = -1;
|
Drv::SocketDescriptor client_fd;
|
||||||
status1 = this->component.configure("127.0.0.1", port, 0, 100);
|
status1 = this->component.configure("127.0.0.1", port, 0, 100);
|
||||||
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
||||||
|
|
||||||
@ -41,46 +40,45 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
EXPECT_TRUE(this->wait_on_started(true, Drv::Test::get_configured_delay_ms()/10 + 1));
|
EXPECT_TRUE(this->wait_on_started(true, Drv::Test::get_configured_delay_ms()/10 + 1));
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(component.isStarted());
|
EXPECT_TRUE(component.isStarted());
|
||||||
|
|
||||||
// Loop through a bunch of client disconnects
|
// Loop through a bunch of client disconnects
|
||||||
for (U32 i = 0; i < iterations && serverStat == SOCK_SUCCESS; i++) {
|
for (U32 i = 0; i < iterations && status1 == SOCK_SUCCESS; i++) {
|
||||||
Drv::TcpClientSocket client;
|
Drv::TcpClientSocket client;
|
||||||
client.configure("127.0.0.1", this->component.getListenPort(), 0, 100);
|
client.configure("127.0.0.1", this->component.getListenPort(), 0, 100);
|
||||||
status2 = client.open(client_fd);
|
status2 = client.open(client_fd);
|
||||||
|
EXPECT_EQ(status2, Drv::SocketIpStatus::SOCK_SUCCESS) << "Failed to connect client";
|
||||||
U32 size = sizeof(m_data_storage);
|
U32 size = sizeof(m_data_storage);
|
||||||
|
|
||||||
// Not testing with reconnect thread, we will need to open ourselves
|
// Not testing with reconnect thread, we will need to open ourselves
|
||||||
if (not recv_thread) {
|
if (not recv_thread) {
|
||||||
status1 = this->component.open();
|
status1 = this->component.open();
|
||||||
} else {
|
} else {
|
||||||
EXPECT_TRUE(this->wait_on_change(true, Drv::Test::get_configured_delay_ms()/10 + 1));
|
EXPECT_TRUE(this->wait_on_change(true, Drv::Test::get_configured_delay_ms()/10 + 1)) <<
|
||||||
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(this->component.isOpened());
|
EXPECT_TRUE(this->component.isOpened()) <<
|
||||||
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
|
|
||||||
EXPECT_EQ(status1, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status1, Drv::SOCK_SUCCESS) <<
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
|
EXPECT_EQ(status2, Drv::SOCK_SUCCESS) <<
|
||||||
status2 = Drv::SOCK_NO_DATA_AVAILABLE;
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
|
|
||||||
// If all the opens worked, then run this
|
// If all the opens worked, then run this
|
||||||
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
||||||
(this->component.isOpened())) {
|
(this->component.isOpened())) {
|
||||||
// Force the sockets not to hang, if at all possible
|
// Force the sockets not to hang, if at all possible
|
||||||
|
Drv::Test::force_recv_timeout(this->component.m_descriptor.fd, this->component.getSocketHandler());
|
||||||
Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler());
|
Drv::Test::force_recv_timeout(client_fd.fd, client);
|
||||||
Drv::Test::force_recv_timeout(client_fd, client);
|
|
||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
||||||
EXPECT_EQ(status, SendStatus::SEND_OK);
|
EXPECT_EQ(status, SendStatus::SEND_OK) <<
|
||||||
U16 counter = 0;
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
while ((status2 == Drv::SOCK_NO_DATA_AVAILABLE) and counter < Drv::Test::MAX_ITER) {
|
Drv::Test::receive_all(client, client_fd, buffer, size);
|
||||||
status2 = client.recv(client_fd, buffer, size);
|
EXPECT_EQ(status2, Drv::SOCK_SUCCESS) <<
|
||||||
counter++;
|
"On iteration: " << i << " and receive thread: " << recv_thread << " and errno " << errno;
|
||||||
}
|
EXPECT_EQ(size, m_data_buffer.getSize()) <<
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
EXPECT_EQ(size, m_data_buffer.getSize());
|
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||||
|
|
||||||
// If receive thread is live, try the other way
|
// If receive thread is live, try the other way
|
||||||
@ -88,24 +86,30 @@ void TcpServerTester ::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
m_spinner = false;
|
m_spinner = false;
|
||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
status2 = client.send(client_fd, m_data_buffer.getData(), m_data_buffer.getSize());
|
status2 = client.send(client_fd, m_data_buffer.getData(), m_data_buffer.getSize());
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
EXPECT_EQ(status2, Drv::SOCK_SUCCESS) <<
|
||||||
//from_deallocate_handler(0, m_data_buffer);
|
"On iteration: " << i << " and receive thread: " << recv_thread;
|
||||||
while (not m_spinner) {}
|
if (status2 == Drv::SOCK_SUCCESS) {
|
||||||
|
while (not m_spinner) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Properly stop the client on the last iteration
|
// Properly stop the client on the last iteration
|
||||||
if (((1 + i) == iterations) && recv_thread) {
|
if (((1 + i) == iterations) && recv_thread) {
|
||||||
this->component.stop();
|
this->component.stop();
|
||||||
this->component.close();
|
|
||||||
client.close(client_fd); // Client must be closed first or the server risks binding to an existing address
|
|
||||||
this->component.shutdown();
|
|
||||||
this->component.join();
|
this->component.join();
|
||||||
} else {
|
} else {
|
||||||
|
// Server initiates shutdown. It thus must drain its data until it receives
|
||||||
|
// a socket disconnection. Then it can safely close.
|
||||||
|
this->component.shutdown();
|
||||||
|
Drv::Test::drain(this->component.m_socket, this->component.m_descriptor);
|
||||||
this->component.close();
|
this->component.close();
|
||||||
client.close(client_fd); // Client must be closed first or the server risks binding to an existing address
|
|
||||||
}
|
}
|
||||||
|
// Server should have shutdown cleanly and waited for this to be shut down. It is safe
|
||||||
|
// to release the file descriptor.
|
||||||
|
client.close(client_fd);
|
||||||
}
|
}
|
||||||
|
this->component.terminate();
|
||||||
ASSERT_from_ready_SIZE(iterations);
|
ASSERT_from_ready_SIZE(iterations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +172,7 @@ void TcpServerTester ::test_advanced_reconnect() {
|
|||||||
void TcpServerTester ::from_recv_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& recvBuffer, const RecvStatus& recvStatus) {
|
void TcpServerTester ::from_recv_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& recvBuffer, const RecvStatus& recvStatus) {
|
||||||
// this function will still receive a status of error because the recv port is always called
|
// this function will still receive a status of error because the recv port is always called
|
||||||
this->pushFromPortEntry_recv(recvBuffer, recvStatus);
|
this->pushFromPortEntry_recv(recvBuffer, recvStatus);
|
||||||
if (recvStatus == RecvStatus::RECV_OK){
|
if (recvStatus == RecvStatus::RECV_OK) {
|
||||||
// Make sure we can get to unblocking the spinner
|
// Make sure we can get to unblocking the spinner
|
||||||
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
|
EXPECT_EQ(m_data_buffer.getSize(), recvBuffer.getSize()) << "Invalid transmission size";
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
|
Drv::Test::validate_random_buffer(m_data_buffer, recvBuffer.getData());
|
||||||
@ -189,7 +193,6 @@ Fw::Buffer TcpServerTester ::
|
|||||||
{
|
{
|
||||||
this->pushFromPortEntry_allocate(size);
|
this->pushFromPortEntry_allocate(size);
|
||||||
Fw::Buffer buffer(new U8[size], size);
|
Fw::Buffer buffer(new U8[size], size);
|
||||||
m_data_buffer2 = buffer;
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,6 @@ namespace Drv {
|
|||||||
//!
|
//!
|
||||||
TcpServerComponentImpl component;
|
TcpServerComponentImpl component;
|
||||||
Fw::Buffer m_data_buffer;
|
Fw::Buffer m_data_buffer;
|
||||||
Fw::Buffer m_data_buffer2;
|
|
||||||
U8 m_data_storage[SEND_DATA_BUFFER_SIZE];
|
U8 m_data_storage[SEND_DATA_BUFFER_SIZE];
|
||||||
std::atomic<bool> m_spinner;
|
std::atomic<bool> m_spinner;
|
||||||
|
|
||||||
|
|||||||
@ -23,23 +23,16 @@ namespace Drv {
|
|||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
UdpComponentImpl::UdpComponentImpl(const char* const compName)
|
UdpComponentImpl::UdpComponentImpl(const char* const compName)
|
||||||
: UdpComponentBase(compName),
|
: UdpComponentBase(compName) {}
|
||||||
SocketComponentHelper() {}
|
|
||||||
|
|
||||||
SocketIpStatus UdpComponentImpl::configureSend(const char* hostname,
|
SocketIpStatus UdpComponentImpl::configureSend(const char* hostname,
|
||||||
const U16 port,
|
const U16 port,
|
||||||
const U32 send_timeout_seconds,
|
const U32 send_timeout_seconds,
|
||||||
const U32 send_timeout_microseconds) {
|
const U32 send_timeout_microseconds) {
|
||||||
if (not this->isStarted()) {
|
|
||||||
(void)this->startup();
|
|
||||||
}
|
|
||||||
return m_socket.configureSend(hostname, port, send_timeout_seconds, send_timeout_microseconds);
|
return m_socket.configureSend(hostname, port, send_timeout_seconds, send_timeout_microseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
SocketIpStatus UdpComponentImpl::configureRecv(const char* hostname, const U16 port) {
|
SocketIpStatus UdpComponentImpl::configureRecv(const char* hostname, const U16 port) {
|
||||||
if (not this->isStarted()) {
|
|
||||||
(void)this->startup();
|
|
||||||
}
|
|
||||||
return m_socket.configureRecv(hostname, port);
|
return m_socket.configureRecv(hostname, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -147,7 +147,6 @@ PROTECTED:
|
|||||||
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
|
* \return SEND_OK on success, SEND_RETRY when critical data should be retried and SEND_ERROR upon error
|
||||||
*/
|
*/
|
||||||
Drv::SendStatus send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer);
|
Drv::SendStatus send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& fwBuffer);
|
||||||
|
|
||||||
Drv::UdpSocket m_socket; //!< Socket implementation
|
Drv::UdpSocket m_socket; //!< Socket implementation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -4,22 +4,22 @@
|
|||||||
|
|
||||||
#include "UdpTester.hpp"
|
#include "UdpTester.hpp"
|
||||||
|
|
||||||
TEST(Nominal, BasicMessaging) {
|
TEST(Nominal, UdpBasicMessaging) {
|
||||||
Drv::UdpTester tester;
|
Drv::UdpTester tester;
|
||||||
tester.test_basic_messaging();
|
tester.test_basic_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Nominal, BasicReceiveThread) {
|
TEST(Nominal, UdpBasicReceiveThread) {
|
||||||
Drv::UdpTester tester;
|
Drv::UdpTester tester;
|
||||||
tester.test_receive_thread();
|
tester.test_receive_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, MultiMessaging) {
|
TEST(Reconnect, UdpMultiMessaging) {
|
||||||
Drv::UdpTester tester;
|
Drv::UdpTester tester;
|
||||||
tester.test_multiple_messaging();
|
tester.test_multiple_messaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Reconnect, ReceiveThreadReconnect) {
|
TEST(Reconnect, UdpReceiveThreadReconnect) {
|
||||||
Drv::UdpTester tester;
|
Drv::UdpTester tester;
|
||||||
tester.test_advanced_reconnect();
|
tester.test_advanced_reconnect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
U8 buffer[sizeof(m_data_storage)] = {};
|
U8 buffer[sizeof(m_data_storage)] = {};
|
||||||
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status1 = Drv::SOCK_SUCCESS;
|
||||||
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
Drv::SocketIpStatus status2 = Drv::SOCK_SUCCESS;
|
||||||
NATIVE_INT_TYPE udp2_fd = -1;
|
Drv::SocketDescriptor udp2_fd;
|
||||||
|
|
||||||
U16 port1 = Drv::Test::get_free_port(true);
|
U16 port1 = Drv::Test::get_free_port(true);
|
||||||
ASSERT_NE(0, port1);
|
ASSERT_NE(0, port1);
|
||||||
@ -83,25 +83,17 @@ void UdpTester::test_with_loop(U32 iterations, bool recv_thread) {
|
|||||||
<< "Port1: " << port1 << std::endl
|
<< "Port1: " << port1 << std::endl
|
||||||
<< "Port2: " << port2 << std::endl;
|
<< "Port2: " << port2 << std::endl;
|
||||||
|
|
||||||
status2 = Drv::SOCK_NO_DATA_AVAILABLE;
|
|
||||||
|
|
||||||
// If all the opens worked, then run this
|
// If all the opens worked, then run this
|
||||||
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
if ((Drv::SOCK_SUCCESS == status1) && (Drv::SOCK_SUCCESS == status2) &&
|
||||||
(this->component.isOpened())) {
|
(this->component.isOpened())) {
|
||||||
// Force the sockets not to hang, if at all possible
|
// Force the sockets not to hang, if at all possible
|
||||||
Drv::Test::force_recv_timeout(this->component.m_fd, this->component.getSocketHandler());
|
Drv::Test::force_recv_timeout(this->component.m_descriptor.fd, this->component.getSocketHandler());
|
||||||
Drv::Test::force_recv_timeout(udp2_fd, udp2);
|
Drv::Test::force_recv_timeout(udp2_fd.fd, udp2);
|
||||||
m_data_buffer.setSize(sizeof(m_data_storage));
|
m_data_buffer.setSize(sizeof(m_data_storage));
|
||||||
Drv::Test::fill_random_buffer(m_data_buffer);
|
size = Drv::Test::fill_random_buffer(m_data_buffer);
|
||||||
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
Drv::SendStatus status = invoke_to_send(0, m_data_buffer);
|
||||||
EXPECT_EQ(status, SendStatus::SEND_OK);
|
EXPECT_EQ(status, SendStatus::SEND_OK);
|
||||||
U16 counter = 0;
|
Drv::Test::receive_all(udp2, udp2_fd, buffer, size);
|
||||||
while ((status2 == Drv::SOCK_NO_DATA_AVAILABLE) and counter < Drv::Test::MAX_ITER) {
|
|
||||||
status2 = udp2.recv(udp2_fd, buffer, size);
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
EXPECT_EQ(status2, Drv::SOCK_SUCCESS);
|
|
||||||
EXPECT_EQ(size, m_data_buffer.getSize());
|
|
||||||
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
Drv::Test::validate_random_buffer(m_data_buffer, buffer);
|
||||||
// If receive thread is live, try the other way
|
// If receive thread is live, try the other way
|
||||||
if (recv_thread) {
|
if (recv_thread) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user