Skip to content

Commit

Permalink
Merge pull request #8 from Kilemonn/concurrent-bind-tests
Browse files Browse the repository at this point in the history
Prebind callback on UDP Socket + Concurrent bind tests
  • Loading branch information
Kilemonn authored Nov 11, 2024
2 parents b2f1c99 + 5ab208c commit 4dcfee1
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PROJECT_NAME CppSocketLibrary)

project(${PROJECT_NAME} VERSION 0.5.3)
project(${PROJECT_NAME} VERSION 0.5.4)

set(HEADERS
src/serversocket/ServerSocket.h
Expand Down
2 changes: 2 additions & 0 deletions Environment-Test-Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# docker build -f .\Environment-Test-Dockerfile .

FROM alpine:3.20.0 AS alpine

WORKDIR /alpine
Expand Down
1 change: 0 additions & 1 deletion src/serversocket/ServerSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ namespace kt
void initialisePortNumber();

public:
ServerSocket() = default;
ServerSocket(const kt::SocketType, const std::optional<std::string>& = std::nullopt, const unsigned short& = 0, const unsigned int& = 20, const kt::InternetProtocolVersion = kt::InternetProtocolVersion::Any);
ServerSocket(const kt::ServerSocket&);
kt::ServerSocket& operator=(const kt::ServerSocket&);
Expand Down
10 changes: 10 additions & 0 deletions src/socket/UDPSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ namespace kt

#endif

if (preBindSocketOperation.has_value())
{
preBindSocketOperation.value()(this->receiveSocket);
}

int bindResult = ::bind(this->receiveSocket, &firstAddress.address, kt::getAddressLength(firstAddress));
this->bound = bindResult != -1;
if (!this->bound)
Expand Down Expand Up @@ -223,6 +228,11 @@ namespace kt
this->preSendSocketOperation = std::make_optional(newOperation);
}

void UDPSocket::setPreBindSocketOperation(std::function<void(SOCKET&)> newOperation)
{
this->preBindSocketOperation = std::make_optional(newOperation);
}

int UDPSocket::pollSocket(SOCKET socket, const long& timeout) const
{
if (kt::isInvalidSocket(socket))
Expand Down
2 changes: 2 additions & 0 deletions src/socket/UDPSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace kt
kt::InternetProtocolVersion protocolVersion = kt::InternetProtocolVersion::Any;
std::optional<unsigned short> listeningPort = std::nullopt;
std::optional<std::function<void(SOCKET&)>> preSendSocketOperation = std::nullopt;
std::optional<std::function<void(SOCKET&)>> preBindSocketOperation = std::nullopt;

int pollSocket(SOCKET socket, const long& = 1000) const;
void initialiseListeningPortNumber();
Expand Down Expand Up @@ -79,6 +80,7 @@ namespace kt
std::optional<unsigned short> getListeningPort() const;

void setPreSendSocketOperation(std::function<void(SOCKET&)>);
void setPreBindSocketOperation(std::function<void(SOCKET&)>);
};

} // End namespace kt
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ set(SOURCE
socket/BluetoothSocketTest.cpp

address/SocketAddressTest.cpp

socket/ScenarioTest.cpp
)

add_executable(${PROJECT_NAME} ${SOURCE})
Expand Down
85 changes: 85 additions & 0 deletions tests/socket/ScenarioTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

#include <gtest/gtest.h>

#include "../../src/socket/UDPSocket.h"
#include "../../src/serversocket/ServerSocket.h"

namespace kt
{
/**
* Ensure that we can bind to the same port using TCP and UDP sockets.
*
* In this case, when UDP binds first.
*/
TEST(ScenarioTest, UDPThenTCPBindSamePort)
{
kt::UDPSocket socket;
std::pair<bool, kt::SocketAddress> bindResult = socket.bind();
ASSERT_TRUE(bindResult.first);
ASSERT_NE(std::nullopt, socket.getListeningPort());

kt::ServerSocket server(SocketType::Wifi, std::nullopt, socket.getListeningPort().value());

ASSERT_EQ(server.getPort(), socket.getListeningPort().value());

server.close();
socket.close();
}

/**
* Ensure that we can bind to the same port using TCP and UDP sockets.
*
* In this case, when TCP binds first.
*/
TEST(ScenarioTest, TCPThenUDPBindSamePort)
{
kt::ServerSocket server(SocketType::Wifi);

kt::UDPSocket socket;
std::pair<bool, kt::SocketAddress> bindResult = socket.bind(std::nullopt, server.getPort());
ASSERT_TRUE(bindResult.first);
ASSERT_NE(std::nullopt, socket.getListeningPort());

ASSERT_EQ(server.getPort(), socket.getListeningPort().value());

server.close();
socket.close();
}

/**
* Ensure that we can bind two UDP sockets to the same address by setting SO_REUSEADDR in the pre-bind handler.
*/
TEST(ScenarioTest, TwoUDPSocketsBindingToSamePort)
{
std::function setReuseAddrOption = [](SOCKET& s) {
const int enableOption = 1;
ASSERT_EQ(0, setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&enableOption, sizeof(enableOption)));
};

kt::UDPSocket socket;
socket.setPreBindSocketOperation(setReuseAddrOption);
std::pair<bool, kt::SocketAddress> bindResult = socket.bind();
ASSERT_TRUE(bindResult.first);

kt::UDPSocket socket2;
socket2.setPreBindSocketOperation(setReuseAddrOption);
bindResult = socket2.bind(std::nullopt, socket.getListeningPort().value());
ASSERT_TRUE(bindResult.first);

ASSERT_EQ(socket.getListeningPort().value(), socket2.getListeningPort().value());

kt::UDPSocket sendSocket;
const std::string data = "TwoUDPSocketsBindingToSamePort";
std::pair<std::pair<bool, int>, kt::SocketAddress> sendResult = sendSocket.sendTo("localhost", socket.getListeningPort().value(), data);
ASSERT_TRUE(sendResult.first.first);

// Make sure only one of the sockets is ready to read, not both
ASSERT_FALSE(socket.ready() && socket2.ready());
ASSERT_TRUE(socket2.ready() || socket.ready());

socket.close();
socket2.close();

sendSocket.close();
}
}

0 comments on commit 4dcfee1

Please sign in to comment.