Skip to content

Instantly share code, notes, and snippets.

@Sergeydigl3
Created May 21, 2024 17:35
Show Gist options
  • Select an option

  • Save Sergeydigl3/8328de6ad27e02c80d85cd3960c1a205 to your computer and use it in GitHub Desktop.

Select an option

Save Sergeydigl3/8328de6ad27e02c80d85cd3960c1a205 to your computer and use it in GitHub Desktop.
boost asio udp proxy simulate network issues, latency, packet loss for tests
//
// Created by Serge on 20.05.2024.
//
#include <gtest/gtest.h>
#include <iostream>
#include "protocol/STIP.h"
#include "protocol/Session.h"
#include "protocol/Connection.h"
#include "server/STIPServer.h"
#include "client/STIPClient.h"
#include "protocol/STIPVersion.h"
#include <iostream>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <thread>
#include <random>
#include "protocol/errors/STIP_errors.h"
#include <boost/bind/bind.hpp>
#include <random>
#include <utility>
using namespace std;
using namespace STIP;
using boost::asio::ip::udp;
#define DEF_PROXY_PORT 12226
#define DEF_SERVER_PORT 12223
#define DEF_CLIENT_PORT 12224
constexpr int BUFFER_SIZE = 1024;
class UdpProxy {
public:
UdpProxy(int client_port, int server_port, int proxy_port)
: io_context_(), socket_(io_context_), client_port_(client_port), server_port_(server_port),
proxy_port_(proxy_port) {}
void start() {
is_running_ = true;
try {
// Создание UDP сокета для прокси сервера
socket_.open(udp::v4());
socket_.bind(udp::endpoint(udp::v4(), proxy_port_));
// Адрес клиента и сервера
client_endpoint_ = udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), client_port_);
server_endpoint_ = udp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), server_port_);
// Основной цикл прокси сервера
while (true) {
udp::endpoint remote_endpoint;
boost::system::error_code error;
std::size_t len = socket_.receive_from(boost::asio::buffer(recv_buffer_), remote_endpoint, 0, error);
if (!is_running_) break;
if (error && error != boost::asio::error::message_size) {
std::cerr << "Receive failed: " << error.message() << std::endl;
continue;
}
// delay
std::this_thread::sleep_for(std::chrono::milliseconds(delay_));
// drop packet
std::uniform_real_distribution<float> distribution(0.0, 100.0);
float random_number = distribution(generator);
if (random_number < loss_percentage_) {
continue;
}
// Определение направления и пересылка данных
if (remote_endpoint == client_endpoint_) {
// Данные от клиента к серверу
socket_.send_to(boost::asio::buffer(recv_buffer_, len), server_endpoint_, 0, error);
if (error) {
std::cerr << "Send to server failed: " << error.message() << std::endl;
}
} else if (remote_endpoint == server_endpoint_) {
// Данные от сервера к клиенту
socket_.send_to(boost::asio::buffer(recv_buffer_, len), client_endpoint_, 0, error);
if (error) {
std::cerr << "Send to client failed: " << error.message() << std::endl;
}
} else {
std::cerr << "Unknown sender: " << remote_endpoint << std::endl;
}
}
} catch (std::exception &e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
}
void async_start() {
if (is_running_) return;
is_running_ = true;
async_thread_ = std::thread([this] { start(); });
}
void async_stop() {
if (!is_running_) return;
is_running_ = false;
socket_.close();
if (async_thread_.joinable()) {
async_thread_.join();
}
// socket_.close();
}
void setDelay(int delay) {
delay_ = delay;
}
void setLossPercentage(float loss_percentage) {
loss_percentage_ = loss_percentage;
}
~UdpProxy() {
async_stop();
}
private:
boost::asio::io_context io_context_;
udp::socket socket_;
udp::endpoint client_endpoint_;
udp::endpoint server_endpoint_;
boost::array<char, BUFFER_SIZE> recv_buffer_;
std::thread async_thread_;
bool is_running_ = false;
// atomic delay
std::atomic<int> delay_ = 0;
std::atomic<float> loss_percentage_ = 0;
std::default_random_engine generator;
int client_port_;
int server_port_;
int proxy_port_;
bool test_1_skip_command__3 = false;
int test_1_skip_counter = 0;
int test_1_skip_target_count;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment