Skip to content

Commit

Permalink
Merge pull request #4 from Kilemonn/refactor-for-individual-tcp-and-u…
Browse files Browse the repository at this point in the history
…dp-sockets

Refactor for individual tcp and udp sockets
  • Loading branch information
Kilemonn authored Jun 28, 2024
2 parents d56b564 + aba0698 commit 2706418
Show file tree
Hide file tree
Showing 26 changed files with 1,716 additions and 1,570 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ TestBluetooth

# Build folder
**/build/
**/build-linux/
**/build-*/

cmake-build-debug/*
CMakeFiles/*
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,25 @@ project(${PROJECT_NAME} VERSION 1.0)
set(HEADERS
src/serversocket/ServerSocket.h
src/socket/Socket.h
src/socket/TCPSocket.h
src/socket/UDPSocket.h
src/socket/BluetoothSocket.h
src/address/SocketAddress.h
src/socketexceptions/BindingException.hpp
src/socketexceptions/SocketException.hpp
src/socketexceptions/TimeoutException.hpp
src/socketexceptions/SocketError.h

src/enums/SocketProtocol.h
src/enums/SocketType.h
src/enums/InternetProtocolVersion.h
)

set(SOURCE
src/serversocket/ServerSocket.cpp
src/socket/Socket.cpp
src/socket/TCPSocket.cpp
src/socket/UDPSocket.cpp
src/socket/BluetoothSocket.cpp
src/socketexceptions/SocketError.cpp
src/address/SocketAddress.cpp
)
Expand Down
89 changes: 55 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,65 @@ The following **linux** dependencies are required:
- TCP Example using IPV6:

```cpp
// Create a new Wifi ServerSocket
kt::ServerSocket server(kt::SocketType::Wifi, 56756, 20, InternetProtocolVersion::IPV6);

// Create new TCP socket
kt::Socket client("::1", server.getPort(), kt::SocketType::Wifi, kt::SocketProtocol::TCP);

// Accept connection to server
kt::Socket serverSocket = server.accept();

const std::string testString = "Test";
serverSocket.send(testString);
const std::string response = client.receiveAmount(testString.size());
// Compare received and sent string values
assert(response == testString);

client.close();
serverSocket.close();
server.close();
void tcpExample()
{
// Create a new Wifi ServerSocket
kt::ServerSocket server(kt::SocketType::Wifi, 56756, 20, kt::InternetProtocolVersion::IPV6);

// Create new TCP socket
kt::TCPSocket client("::1", server.getPort());

// Accept the incoming connection at the server
kt::TCPSocket serverSocket = server.acceptTCPConnection();

// Send string with text before and after the delimiter
const std::string testString = "TCP Delimiter Test";
const char delimiter = '~';
if (!socket.send(testString + delimiter + "other string"))
{
std::cout << "Failed to send test string" << std::endl;
return;
}

if (serverSocket.ready())
{
std::string response = serverSocket.receiveToDelimiter(delimiter);
// Check that the received string is the same as the string sent by the client
ASSERT_EQ(response, testString);
}

// Close all sockets
client.close();
serverSocket.close();
server.close();
}
```

- UDP Example using IPV4 (the default protocol version - so protocol related arguments are omitted):

```cpp
kt::Socket serverSocket("127.0.0.1", 43567, kt::SocketType::Wifi, kt::SocketProtocol::UDP);
// Which ever socket is acting as the "server" needs to bind, only a single process can be bound
// to a specific port at a time
serverSocket.bind(InternetProtocolVersion::IPV4); // This argument can be removed as the default is `InternetProtocolVersion::IPV6`, setting arg here explicitly for clarity.
kt::Socket client("127.0.0.1", 43567, kt::SocketType::Wifi, kt::SocketProtocol::UDP);
const std::string testString = "UDP Test";
const char delimiter = '~';
client.send(testString + delimiter);
const std::string response = serverSocket.receiveToDelimiter(delimiter);
assert(response == testString);
serverSocket.close();
client.close();
void udpExample()
{
// The socket receiving data must first be bound
kt::UDPSocket socket;
socket.bind(87893, kt::InternetProtocolVersion::IPV4);

kt::UDPSocket client;
const std::string testString = "UDP test string";
if (!client.sendTo("localhost", 87893, testString).first)
{
std::cout << "Failed to send to address." << std::endl;
return;
}

if (socket.ready())
{
std::pair<std::optional<std::string>, std::pair<int, kt::SocketAddress>> recieved = socket.receiveFrom(testString.size());
ASSERT_EQ(testString, recieved.first.value());
}

socket.close();
}
```

## Known Issues
Expand All @@ -92,4 +113,4 @@ signal(SIGPIPE, SIG_IGN);
### NOTE: UDP Read Sizes
- Take care when reading UDP messages. If you do not read the entire length of the message the rest of the data will be lost. Try using `receiveAll()`/`recieveToDelimiter()`/`receiveAmount()` instead of `get()`, unless you know the amount of data that you are expecting.
- Take care when reading UDP messages. If you do not read the entire length of the message the rest of the data will be lost.
63 changes: 58 additions & 5 deletions src/address/SocketAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,34 @@

#include <optional>
#include <string>
#include <vector>

namespace kt
{
kt::InternetProtocolVersion getInternetProtocolVersion(const kt::SocketAddress& address)
{
return static_cast<kt::InternetProtocolVersion>(address.address.sa_family);
kt::InternetProtocolVersion resolvedVersion = static_cast<kt::InternetProtocolVersion>(address.address.sa_family);

if (resolvedVersion == kt::InternetProtocolVersion::IPV4 || resolvedVersion == kt::InternetProtocolVersion::IPV6)
{
return resolvedVersion;
}
return kt::InternetProtocolVersion::Any;
}

long getPortNumber(const kt::SocketAddress& address)
unsigned int getPortNumber(const kt::SocketAddress& address)
{
kt::InternetProtocolVersion version = getInternetProtocolVersion(address);
if (version == kt::InternetProtocolVersion::IPV6)
{
return htonl(address.ipv6.sin6_port);
return htons(address.ipv6.sin6_port);
}
// I believe the address is in the same position for ipv4 and ipv6 structs, so it doesn't really matter.
// Doing the checks anway to make sure its fine
return htonl(address.ipv4.sin_port);
return htons(address.ipv4.sin_port);
}

std::optional<std::string> resolveToAddress(const kt::SocketAddress& address)
std::optional<std::string> getAddress(const kt::SocketAddress& address)
{
const kt::InternetProtocolVersion protocolVersion = getInternetProtocolVersion(address);
const size_t addressLength = protocolVersion == kt::InternetProtocolVersion::IPV6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN;
Expand All @@ -47,4 +54,50 @@ namespace kt
// Since we zero out the address, we need to check its not default initialised
return !asString.empty() && asString != "0.0.0.0" && asString != "::" ? std::optional<std::string>{asString} : std::nullopt;
}

std::pair<std::optional<kt::SocketAddress>, int> socketToAddress(const SOCKET& socket)
{
kt::SocketAddress address{};
socklen_t socketSize = sizeof(address);
int result = getsockname(socket, &address.address, &socketSize);
return std::make_pair(result == -1 ? std::nullopt : std::make_optional(address), result);
}

std::pair<std::vector<kt::SocketAddress>, int> resolveToAddresses(const std::optional<std::string>& hostname, const unsigned short& port, addrinfo& hints)
{
std::vector<kt::SocketAddress> addresses;
addrinfo* resolvedAddresses = nullptr;

int result = getaddrinfo(hostname.has_value() ? hostname.value().c_str() : nullptr, std::to_string(port).c_str(), &hints, &resolvedAddresses);
if (result != 0 || resolvedAddresses == nullptr)
{
if (resolvedAddresses != nullptr)
{
freeaddrinfo(resolvedAddresses);
}
return std::make_pair(addresses, result);
}

// We need to iterate over the resolved address and attempt to connect to each of them, if a connection attempt is succesful
// we will return, otherwise we will throw is we are unable to connect to any.
for (addrinfo* addr = resolvedAddresses; addr != nullptr; addr = addr->ai_next)
{
kt::SocketAddress address = {};
std::memcpy(&address, addr->ai_addr, addr->ai_addrlen);
addresses.push_back(address);
}
freeaddrinfo(resolvedAddresses);

return std::make_pair(addresses, result);
}

#ifdef _WIN32
int getAddressLength(const kt::SocketAddress& address)
#else
socklen_t getAddressLength(const kt::SocketAddress& address)
#endif
{
const kt::InternetProtocolVersion protocolVersion = getInternetProtocolVersion(address);
return protocolVersion == kt::InternetProtocolVersion::IPV4 ? sizeof(address.ipv4) : sizeof(address.ipv6);
}
}
25 changes: 22 additions & 3 deletions src/address/SocketAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

#include <string>
#include <optional>
#include <vector>
#include <cstring>

#ifdef _WIN32

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#define _WIN32_WINNT 0x0600
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif

#include <WinSock2.h>
#include <ws2bth.h>
Expand All @@ -24,6 +28,11 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>

// Typedef to match the windows typedef since they are different underlying types
typedef int SOCKET;

#endif

Expand All @@ -41,7 +50,17 @@ namespace kt

kt::InternetProtocolVersion getInternetProtocolVersion(const kt::SocketAddress&);

long getPortNumber(const kt::SocketAddress&);
unsigned int getPortNumber(const kt::SocketAddress&);

std::optional<std::string> getAddress(const kt::SocketAddress&);

std::pair<std::optional<kt::SocketAddress>, int> socketToAddress(const SOCKET&);

std::optional<std::string> resolveToAddress(const kt::SocketAddress&);
std::pair<std::vector<kt::SocketAddress>, int> resolveToAddresses(const std::optional<std::string>&, const unsigned short&, addrinfo&);

#ifdef _WIN32
int getAddressLength(const kt::SocketAddress&);
#else
socklen_t getAddressLength(const kt::SocketAddress&);
#endif
}
11 changes: 0 additions & 11 deletions src/enums/SocketProtocol.h

This file was deleted.

Loading

0 comments on commit 2706418

Please sign in to comment.