Skip to content

Commit

Permalink
feat(core): impl network TcpSocket class
Browse files Browse the repository at this point in the history
  • Loading branch information
roby2014 committed Jul 2, 2024
1 parent 542ec25 commit 7689080
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ set(CUBOS_CORE_SOURCE
"src/geom/box.cpp"
"src/geom/capsule.cpp"
"src/geom/intersections.cpp"

"src/net/tcp_socket.cpp"
)

# Create core library
Expand Down
72 changes: 72 additions & 0 deletions core/include/cubos/core/net/tcp_socket.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/// @file
/// @brief Class @ref cubos::core::net::TcpSocket.
/// @ingroup core-net

#pragma once

#include <cstdint>

#include <cubos/core/api.hpp>
#include <cubos/core/net/address.hpp>

namespace cubos::core::net
{
/// @brief Represents a TCP socket.
/// @ingroup core-net
class CUBOS_CORE_API TcpSocket
{
public:
/// @brief Represents a result code.
enum class ResultCode
{
Success,
ConnectError,
SendError,
ReceiveError,
UnknownError
};

/// @brief Constructs a empty TCP socket.
TcpSocket();

/// @brief Deconstructs by desconnecting the inner socket.
~TcpSocket();

/// @brief Forbid copy construction.
TcpSocket(const TcpSocket&) = delete;

/// @brief Forbid copy assignment.
TcpSocket& operator=(const TcpSocket&) = delete;

/// @brief Connects via TCP to a remote `address` and `port`.
/// @param address Destination address.
/// @param port Destination port.
/// @param timeoutMs Connection timeout in milliseconds.
/// @return Result code.
ResultCode connect(const Address& address, uint16_t port, int timeoutMs = 0);

/// @brief Sends `data` to the connected socket.
/// @param data Message.
/// @param size Message size.
/// @return Result code.
ResultCode send(const void* data, size_t size);

/// @brief Receives a single datagram message on the socket.
/// @param data Buffer to store the datagram message.
/// @param size Maximum buffer size.
/// @param[out] received Number of bytes read.
/// @return Result code.
ResultCode receive(void* data, size_t size, ssize_t& received);

/// @brief Control `blocking` mode of inner socket.
/// @param blocking Whether to block or not.
/// @note A blocking socket does not return control until it has sent (or received) some or all data specified
/// for the operation
/// @note A non-blocking socket returns whatever is in the receive buffer and immediately continues.
void setBlocking(bool blocking);

private:
int mSock;
bool mBlocking;
};
} // namespace cubos::core::net
89 changes: 89 additions & 0 deletions core/src/net/tcp_socket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cubos/core/log.hpp>
#include <cubos/core/net/tcp_socket.hpp>

using cubos::core::net::Address;
using cubos::core::net::TcpSocket;

TcpSocket::TcpSocket()
: mSock(-1)
, mBlocking(true)
{
}

TcpSocket::~TcpSocket()
{
}

TcpSocket::ResultCode TcpSocket::connect(const Address& address, uint16_t port, int timeoutMs)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(address.toString().c_str());
addr.sin_port = htons(port);

if (::connect(mSock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
CUBOS_ERROR("Could not connect to address {} at port '{}'", address.toString(), port);
return ResultCode::ConnectError;
}

(void)timeoutMs;
// TODO implement timeout

CUBOS_INFO("New TCP connection at address {} and port '{}'", address.toString(), port);
return ResultCode::Success;
}

TcpSocket::ResultCode TcpSocket::send(const void* data, size_t size)
{
if (::send(mSock, data, size, 0) < 0)
{
CUBOS_ERROR("Could not send TCP message with socket '{}'", mSock);
return ResultCode::SendError;
}

CUBOS_INFO("Sent TCP message");
return ResultCode::Success;
}

TcpSocket::ResultCode TcpSocket::receive(void* data, size_t size, ssize_t& received)
{
received = ::recv(mSock, data, size, 0);
if (received < 0)
{
CUBOS_ERROR("Could not receive TCP message with socket '{}'", mSock);
return ResultCode::ReceiveError;
}

CUBOS_INFO("Received TCP message");
return ResultCode::Success;
}

void TcpSocket::setBlocking(bool blocking)
{
mBlocking = blocking;

int flags = fcntl(mSock, F_GETFL, 0);
if (flags == -1)
{
return;
}

if (blocking)
{
flags &= ~O_NONBLOCK;
}
else
{
flags |= O_NONBLOCK;
}

fcntl(mSock, F_SETFL, flags);
CUBOS_INFO("TCP socket '{}' is now in '{}' mode", mSock, blocking ? "blocking" : "non-blocking");
}

0 comments on commit 7689080

Please sign in to comment.