Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.20)

project(udp_server)

set(udp_server main.cpp)

#source_group(source FILES ${${PROJECT_NAME}_SRC})

add_executable(main.exe main.cpp socket_wrapper.cpp socket.cpp)
target_link_libraries(main.exe ws2_32.lib)

#if(WIN32)
# target_link_libraries(udp_server wsock32 ws2_32)
#endif()

107 changes: 107 additions & 0 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <algorithm>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <string>

#include "socket_headers.h"
#include "socket_wrapper.h"
#include "socket_class.h"
#include "socket_wrapper_windows.h"

// Trim from end (in place).
static inline std::string& rtrim(std::string& s)
{
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) { return !std::isspace(c); }).base(), s.end());
return s;
}


int main(int argc, char const *argv[])
{

if (argc != 2)
{
std::cout << "Usage: " << argv[0] << " <port>" << std::endl;
return EXIT_FAILURE;
}

socket_wrapper::SocketWrapper sock_wrap;
const int port { std::stoi(argv[1]) };

socket_wrapper::Socket sock = {AF_INET, SOCK_DGRAM, IPPROTO_UDP};

std::cout << "Starting echo server on the port " << port << "...\n";

if (!sock)
{
std::cerr << sock_wrap.get_last_error_string() << std::endl;
return EXIT_FAILURE;
}

sockaddr_in addr =
{
.sin_family = PF_INET,
.sin_port = htons(port),
};

addr.sin_addr.s_addr = INADDR_ANY;

if (bind(sock, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) != 0)
{
std::cerr << sock_wrap.get_last_error_string() << std::endl;
// Socket will be closed in the Socket destructor.
return EXIT_FAILURE;
}

char buffer[256];

// socket address used to store client address
struct sockaddr_in client_address = {0};
socklen_t client_address_len = sizeof(sockaddr_in);
ssize_t recv_len = 0;

std::cout << "Running echo server...\n" << std::endl;
char client_address_buf[INET_ADDRSTRLEN];

while (true)
{
// Read content into buffer from an incoming client.
recv_len = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
reinterpret_cast<sockaddr *>(&client_address),
&client_address_len);

if (recv_len > 0)
{
buffer[recv_len] = '\0';
std::cout
<< "Client with address "
<< inet_ntoa( client_address.sin_addr )
<< ":" << ntohs(client_address.sin_port)
<< " sent datagram "
<< "[length = "
<< recv_len
<< "]:\n'''\n"
<< buffer
<< "\n'''"
<< std::endl;
std::string command_string = {buffer, 0, recv_len};
if ("exit" == command_string)
EXIT_SUCCESS;
send(sock, buffer, recv_len, 0);


rtrim(command_string);
std::cout << command_string << std::endl;

// Send same content back to the client ("echo").
sendto(sock, buffer, recv_len, 0, reinterpret_cast<const sockaddr *>(&client_address),
client_address_len);
}

std::cout << std::endl;
}

return EXIT_SUCCESS;
}

78 changes: 78 additions & 0 deletions socket.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "socket_class.h"
#include "socket_headers.h"

#include <utility>

#ifdef _WIN32
constexpr auto close_type = SD_BOTH;
# define close_socket closesocket
#else
constexpr auto close_type = SHUT_RDWR;
# define close_socket ::close
#endif


namespace socket_wrapper
{

Socket::Socket(int domain, int type, int protocol) : socket_descriptor_(INVALID_SOCKET)
{
open(domain, type, protocol);
}


Socket::Socket(SocketDescriptorType socket_descriptor) : socket_descriptor_(socket_descriptor)
{
}


Socket::Socket(Socket &&s)
{
socket_descriptor_ = s.socket_descriptor_;
s.socket_descriptor_ = INVALID_SOCKET;
}


Socket &Socket::operator=(Socket &&s)
{
if (&s == this) return *this;

if (opened()) close();
std::swap(socket_descriptor_, s.socket_descriptor_);

return *this;
}


Socket::~Socket()
{
if (opened()) close();
}


bool Socket::opened() const
{
return socket_descriptor_ != INVALID_SOCKET;
}


void Socket::open(int domain, int type, int protocol)
{
if (opened()) close();
socket_descriptor_ = socket(domain, type, protocol);
}


int Socket::close()
{
int status = 0;

status = close_socket(socket_descriptor_);
socket_descriptor_ = INVALID_SOCKET;

return status;

}

}

38 changes: 38 additions & 0 deletions socket_class.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include "socket_headers.h"

namespace socket_wrapper
{

class Socket
{
public:
Socket(int domain, int type, int protocol);
Socket(SocketDescriptorType socket_descriptor);

Socket(const Socket&) = delete;
Socket(Socket &&s);
Socket &operator=(const Socket &s) = delete;
Socket &operator=(Socket &&s);

virtual ~Socket();

public:
bool opened() const;

public:
operator bool() const { return opened(); }
operator SocketDescriptorType() const { return socket_descriptor_; }

public:
int close();

protected:
void open(int domain, int type, int protocol);

private:
SocketDescriptorType socket_descriptor_;
};

} // socket_wrapper
43 changes: 43 additions & 0 deletions socket_headers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#pragma once

extern "C"
{
#ifdef _WIN32
# include <ws2tcpip.h>
# include <winsock2.h>

/* See http://stackoverflow.com/questions/12765743/getaddrinfo-on-win32 */
# if !defined(_WIN32_WINNT)
# define _WIN32_WINNT 0x0501 /* Windows XP. */
# endif
typedef SOCKET SocketDescriptorType;
//typedef int ssize_t;
typedef unsigned long IoctlType;

# if !defined(in_addr_t)
# include <cinttypes>
typedef uint32_t in_addr_t;
# endif

#else
/* Assume that any non-Windows platform uses POSIX-style sockets instead. */
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netdb.h> /* Needed for getaddrinfo() and freeaddrinfo() */
# include <unistd.h> /* Needed for close() */
typedef int SocketDescriptorType;
typedef int IoctlType;

#endif

// Defined for Windows Sockets.
#if !defined(INVALID_SOCKET)
# define INVALID_SOCKET (-1)
#endif

// Defined for Windows Sockets.
#if !defined(SOCKET_ERROR)
# define SOCKET_ERROR (-1)
#endif

}
44 changes: 44 additions & 0 deletions socket_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "socket_wrapper.h"

#ifdef _WIN32
# include "socket_wrapper_windows.h"
#else
# include "socket_wrapper_unix.h"
#endif


namespace socket_wrapper
{


SocketWrapper::SocketWrapper() : impl_{std::make_unique<SocketWrapperImpl>()}
{
impl_->initialize();
}


SocketWrapper::~SocketWrapper()
{
impl_->deinitialize();
}


bool SocketWrapper::initialized() const
{
return impl_->initialized();
}


int SocketWrapper::get_last_error_code() const
{
return impl_->get_last_error_code();
}


std::string SocketWrapper::get_last_error_string() const
{
return impl_->get_last_error_string();
}

}

27 changes: 27 additions & 0 deletions socket_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <memory>
#include <string>


namespace socket_wrapper
{

class SocketWrapperImpl;

class SocketWrapper
{
public:
SocketWrapper();
~SocketWrapper();

public:
bool initialized() const;
int get_last_error_code() const;
std::string get_last_error_string() const;

private:
std::unique_ptr<SocketWrapperImpl> impl_;
};

}
15 changes: 15 additions & 0 deletions socket_wrapper_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <string>


class ISocketWrapperImpl
{
public:
virtual void initialize() = 0;
virtual bool initialized() const = 0;
virtual void deinitialize() = 0;
virtual int get_last_error_code() const = 0;
virtual std::string get_last_error_string() const = 0;
};

Loading