Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repaired master #45

Closed
wants to merge 7 commits into from
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ build
.vscode
*.log
test.html
TODO
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

143 changes: 104 additions & 39 deletions README.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ include(cmake/MakeSample.cmake)

add_subdirectory(utils)

set(LauncherSource ${CMAKE_CURRENT_SOURCE_DIR}/Launcher.cpp)
option(BUILD_MinimalCppSocket_AVOID_CUSTOM_TARGET "" OFF)

add_subdirectory(tcp)
add_subdirectory(udp)
106 changes: 106 additions & 0 deletions samples/Launcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#if _WIN64 || _WIN32
#include <windows.h>
#endif

class Popen {
public:
Popen(const std::string &line) {
fp =
#if _WIN64 || _WIN32
_popen(line.c_str(), "r")
#elif __linux__
popen(line.c_str(), "r")
#endif
;
if (fp == NULL) {
throw std::runtime_error{"Unable to start the child process"};
}
}

struct CompletedProcess {
int returnCode;
std::string out;
};
CompletedProcess communicate() {
if (!done) {
while (poll()) {
}
done = true;
}
return CompletedProcess{returnCode_, out_.str()};
}

protected:
bool poll() {
std::string temp;
temp.resize(250);
if (fgets(temp.data(), static_cast<int>(temp.size()), fp) == nullptr) {
#if _WIN64 || _WIN32
feof(fp);
#endif

returnCode_ =
#if _WIN64 || _WIN32
_pclose(fp)
#elif __linux__
pclose(fp)
#endif
;

fp = nullptr;
return false;
}
std::size_t last_pos = temp.find('\n');
if (last_pos != std::string::npos) {
temp.resize(last_pos + 1);
}
out_ << temp;
return true;
}

private:
bool done = false;
FILE *fp = nullptr;
std::string buffer_;
int returnCode_ = -1;
std::stringstream out_;
};

#include <string_view>
#include <vector>

const std::string cmd{CMD};
std::vector<std::string_view> getCommands() {
std::vector<std::string_view> res;
std::size_t pos = 0;
while (pos < cmd.size()) {
auto next = cmd.find('|', pos);
if (next == std::string::npos) {
res.emplace_back(cmd.data() + pos);
break;
}
res.emplace_back(cmd.data() + pos, next - pos);
pos = next + 1;
}
return res;
}

int main() {
for (const auto &proc : getCommands()) {
Popen process(std::string{proc.data(), proc.size()});
auto &&[retCode, out] = process.communicate();
std::cout << out;
if (retCode != 0) {
throw std::runtime_error{"Child process didn't complete well"};
}
}

return EXIT_SUCCESS;
}
93 changes: 76 additions & 17 deletions samples/README.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// tcp server
#include <MinimalSocket/tcp/TcpServer.h>
int main() {
MinimalSocket::Port port = 15768; // port the server needs to bind
MinimalSocket::tcp::TcpServer tcp_server(port,
MinimalSocket::AddressFamily::IP_V4);
MinimalSocket::Port port = 15768; // the port to bind
MinimalSocket::tcp::TcpServer<true> tcp_server(
port, MinimalSocket::AddressFamily::IP_V4);

// open the server: binds the port and start to listen on the port
// Open the server. This will bind the port and the server will start to
// listen for connection requests.
bool success = tcp_server.open();

// accepts next client asking connection
MinimalSocket::tcp::TcpConnection accepted_connection =
tcp_server.acceptNewClient(); // blocing till a client actually asks the
// accepts the next client that will ask the connection
MinimalSocket::tcp::TcpConnectionBlocking accepted_connection =
tcp_server.acceptNewClient(); // blocking till a client actually asks the
// connection

// receive a message
Expand All @@ -27,11 +28,13 @@ int main() {
int main() {
MinimalSocket::Port server_port = 15768;
std::string server_address = "192.168.125.85";
MinimalSocket::tcp::TcpClient tcp_client(
MinimalSocket::tcp::TcpClient<true> tcp_client(
MinimalSocket::Address{server_address, server_port});

// open the client: asks connection to server
bool success = tcp_client.open();
// Open the server. Here, the client will ask the connection to specified
// server. After that, the client will be actually connected.
bool success =
tcp_client.open(); // blocking till the connection is actually established

// send a message
tcp_client.send("a message to send");
Expand All @@ -46,10 +49,10 @@ int main() {
#include <MinimalSocket/udp/UdpSocket.h>
int main() {
MinimalSocket::Port this_socket_port = 15768;
MinimalSocket::udp::UdpBinded udp_socket(this_socket_port,
MinimalSocket::udp::Udp<true> udp_socket(this_socket_port,
MinimalSocket::AddressFamily::IP_V6);

// open the client: reserve port for this cocket
// Open the server. This will bind the specified port.
bool success = udp_socket.open();

// send a message to another udp
Expand All @@ -68,11 +71,13 @@ int main() {

MinimalSocket::Address permanent_sender_udp =
MinimalSocket::Address{"192.168.125.85", 15768};
MinimalSocket::udp::UdpConnected udp_connected_socket = udp_socket.connect(
permanent_sender_udp); // ownership of the underlying socket is transfered
// from udp_socket to udp_connected_socket, meaning
// that you can't use anymore udp_socket (unless
// you re-open it)
MinimalSocket::udp::UdpConnected<true> udp_connected_socket =
udp_socket.connect(
permanent_sender_udp); // ownership of the underlying socket is
// transfered from udp_socket to
// udp_connected_socket, meaning that you can't
// use anymore udp_socket (unless you re-open
// it)

// receive a message
std::size_t message_max_size = 1000;
Expand All @@ -82,3 +87,57 @@ int main() {
// send a message
udp_connected_socket.send("a message to send");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// tcp server, non blocking
int main() {
MinimalSocket::Port port = 15768; // the port to bind
MinimalSocket::tcp::TcpServer<false> tcp_server(
port, MinimalSocket::AddressFamily::IP_V4);
tcp_server.open();

// check if a client asked for the connection. If no, the function immediately
// returns a nullopt. On the contrary, the returned optional contains the
// handler to the connected client
std::optional<MinimalSocket::tcp::TcpConnectionBlocking>
maybe_accepted_connection = tcp_server.acceptNewClient();

MinimalSocket::tcp::TcpConnectionNonBlocking accepted_connection_nn_block =
maybe_accepted_connection->turnToNonBlocking();
}

// tcp client, non blocking
int main() {
MinimalSocket::Port server_port = 15768;
std::string server_address = "192.168.125.85";
MinimalSocket::tcp::TcpClient<false> tcp_client(
MinimalSocket::Address{server_address, server_port});
tcp_client.open();

std::size_t message_max_size = 1000;
// non blocking receive: returns immediately with an empty message in case no
// new data were available, or with a non empty message in the contrary case
std::string received_message = tcp_client.receive(message_max_size);
}

// udp socket, non blocking
int main() {
MinimalSocket::Port this_socket_port = 15768;
MinimalSocket::udp::Udp<false> udp_socket(
this_socket_port, MinimalSocket::AddressFamily::IP_V6);
udp_socket.open();

std::size_t message_max_size = 1000;
// non blocking receive: returns immediately with an empty message in case no
// new data were available, or with a non empty message in the contrary case
//
// struct ReceiveStringResult {
// Address sender;
// std::string received_message;
// };
std::optional<MinimalSocket::ReceiveStringResult> received_message =
udp_socket.receive(message_max_size);
}
14 changes: 14 additions & 0 deletions samples/cmake/MakeSample.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,25 @@ function(MakeSample INPUT PREFIX)
set(BIN_LOCATION ${BIN_LOCATION}/$<CONFIG>)
endif()

if (CMAKE_GENERATOR MATCHES "Visual Studio" OR BUILD_MinimalCppSocket_AVOID_CUSTOM_TARGET)
# add_custom_target is not supported by visual studio
add_executable(${TARGET_NAME} ${LauncherSource})
target_compile_definitions(${TARGET_NAME} PUBLIC
-D CMD="${Python3_EXECUTABLE} ${SCRIPT_LOCATION} --cmd ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}|${Python3_EXECUTABLE} ${SCRIPT_LOCATION} --location ${BIN_LOCATION} --cmd ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT} --dest ${CMAKE_CURRENT_BINARY_DIR}/report.html --sleep 0.5"
)
else()
add_custom_target(${TARGET_NAME}-preamble ALL
COMMAND "${Python3_EXECUTABLE}" ${SCRIPT_LOCATION}
"--cmd" "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
)
add_custom_target(${TARGET_NAME} ALL
COMMAND "${Python3_EXECUTABLE}" ${SCRIPT_LOCATION}
"--location" "${BIN_LOCATION}"
"--cmd" "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
"--dest" "${CMAKE_CURRENT_BINARY_DIR}/report.html"
"--sleep" "0.5"
)
add_dependencies(${TARGET_NAME} ${TARGET_NAME}-preamble)
endif()

endfunction()
4 changes: 4 additions & 0 deletions samples/tcp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
MakeApp(TcpClient)
MakeApp(TcpServer)
MakeApp(TcpServerNonBlocking)
MakeApp(TcpRepeater)

MakeSample(Sample01_server_client Tcp)
Expand All @@ -10,3 +11,6 @@ add_dependencies(TcpSample02_server_2_clients TcpClient TcpServer)

MakeSample(Sample03_chain_with_2_repeaters Tcp)
add_dependencies(TcpSample03_chain_with_2_repeaters TcpClient TcpServer TcpRepeater)

MakeSample(Sample04_server_nn_block_2_clients Tcp)
add_dependencies(TcpSample04_server_nn_block_2_clients TcpClient TcpServerNonBlocking)
32 changes: 32 additions & 0 deletions samples/tcp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,36 @@ The above classes of samples can be described as follows:
TcpRepeater2->>TcpClient: forawrd response 1
```

- **TcpSample04_server_nn_block_2_clients** is an example of non blocking tcp server. The application uses one single thread to spin multiple connections. More in detail:
- related config file is [Sample04_server_nn_block_2_clients](./Sample04_server_nn_block_2_clients)
- runs **TcpServerNonBlocking**, creating a tcp server that binds and listen to a specified port
- runs **TcpClient**, creating a first tcp client that connections to the previous server, exchanging messages with it.
- runs **TcpClient**, creating a second tcp client that connections to the previous server, exchanging messages with it with a different frequency.
- the following sequence diagram summarizes this sample
```mermaid
sequenceDiagram
TcpServer->>TcpServer: bind a port
TcpClient1->>TcpServer: ask for connection
TcpServer->>TcpClient1: connection done
TcpClient2->>TcpServer: ask for connection
TcpServer->>TcpClient2: connection done
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpClient1->>TcpServer: request 1
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpServer->>TcpClient1: response 1
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpClient2->>TcpServer: request 1
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpServer->>TcpClient2: response 1
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpClient1->>TcpServer: request 2
TcpServer->>TcpServer: has something arrived from client 1? if so send response
TcpServer->>TcpServer: has something arrived from client 2? if so send response
TcpServer->>TcpClient1: response 2

**TcpServer** and **TcpClient** can be also used as stand alone processes, in order to check connections locally or on a different host.
3 changes: 3 additions & 0 deletions samples/tcp/Sample04_server_nn_block_2_clients
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TcpServerNonBlocking --port 35998 --clients 2
TcpClient --port 35998
TcpClient --port 35998 --rate 400
14 changes: 7 additions & 7 deletions samples/tcp/TcpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ int main(const int argc, const char **argv) {
cout << "----------------------- Client -----------------------" << endl;
PARSE_ARGS

const auto server_host = options->getValue("host", "127.0.0.1");
const auto server_port =
static_cast<MinimalSocket::Port>(options->getIntValue("port"));
const auto rate =
std::chrono::milliseconds{options->getIntValue<250>("rate")};
const auto server_host = options->getValue<std::string>("host", "127.0.0.1");
const auto server_port = options->getValue<MinimalSocket::Port>("port");
const auto rate = options->getValue<std::chrono::milliseconds>(
"rate", std::chrono::milliseconds{250});

const MinimalSocket::Address server_address(server_host, server_port);
MinimalSocket::tcp::TcpClient client(server_address);
MinimalSocket::tcp::TcpClient<true> client(server_address);

cout << "Connecting to " << MinimalSocket::to_string(server_address) << endl;
if (!client.open()) {
Expand All @@ -38,7 +37,8 @@ int main(const int argc, const char **argv) {
}
cout << "Connected" << endl;

MinimalSocket::samples::ask(client, rate, options->getIntValue<5>("cycles"));
MinimalSocket::samples::ask(client, rate,
options->getValue<int>("cycles", 5));

// the connection will be close when destroying the client object
return EXIT_SUCCESS;
Expand Down
Loading
Loading