C ++ TCP Proxy ASIO отсутствуют пакеты - PullRequest
0 голосов
/ 24 октября 2019

Я создаю небольшое приложение для своей компании. Я избавлю вас от деталей, почему, но меня попросили создать прокси-сервер TCP для конкретного приложения и протокола, который у нас есть.

Для этого прокси-сервера будет установлено TCP-соединение с сервером, который будет отправлятьПериодически мы используем данные, и Прокси будет отображать данные, полученные двумя другими приложениями на разных портах. Для этого я использовал библиотеки C ++ и ASIO. Я избавлю вас от полного кода, но вот (что я думаю) важная часть, касающаяся TCP-соединений:

Класс ниже обрабатывает входящие данные, полученные нашим прокси. Полученные данные должны быть отправлены нашим клиентам.

#include "TCPInConnection.h"

namespace Proxy
{
    TCPInConnection::TCPInConnection(asio::io_context& io_context, std::string host, int port) :
        host(host),
        port(port),
        io_context(&io_context),
        socket(io_context)
    {
    }

    TCPInConnection::~TCPInConnection()
    {
        Close();
    }

    //Starts connection to the server
    void TCPInConnection::Start()
    {
        if(!socket.is_open())
            connect();
    }

    void TCPInConnection::Close()
    {
        if (socket.is_open())
            socket.close();
    }

    //Receives a callback. The callback passed should be called everytime a new packet was received
    void TCPInConnection::OnReceive(std::function<std::vector<uint8_t>(std::vector<uint8_t>&)> callback)
    {
        this->callback = callback;
    }

    //Handles the connection. If an error occured while connecting, try connection again. When connecting, call receive handler to read data received
    void TCPInConnection::connect()
    {
        socket.async_connect(asio::ip::tcp::endpoint(asio::ip::address::from_string(host), port), [this](const asio::error_code& error) {
            if (error)
            {
                this->connect();
                return;
            }
            asio::socket_base::receive_buffer_size option(8192 * 40);
            socket.set_option(option);

            receive();
        });
    }
    //Handles the reception of data. For evey incoming packet, it should call itself to receive the next packet. If socket is closed, try to reconnect.
    void TCPInConnection::receive() 
    {
        std::array<char, 4000>* receiveBuffer = new std::array<char, 4000>();
        socket.async_receive(asio::buffer(*receiveBuffer), 0, [this, receiveBuffer](const asio::error_code& error, std::size_t len) {
            if (error == asio::error::eof || error == asio::error::connection_reset)
            {
                if (this->socket.is_open())
                    this->socket.close();
                connect();

                delete receiveBuffer;
                return;
            }

            receive();

            if (len == 0)
            {           
                delete receiveBuffer;
                return;
            }

            std::vector<uint8_t> packet(receiveBuffer->begin(), receiveBuffer->begin() + len);
            auto data = callback(packet);
            delete receiveBuffer;

            if (data.size() == 0)
                return;

            std::vector<uint8_t>* output = new std::vector<uint8_t>(data.begin(), data.end());
            socket.async_write_some(asio::buffer(*output), [output](const asio::error_code& error, std::size_t bytes_transferred) {
                delete output;
            });
        });
    }
}

Следующий класс обрабатывает вывод для наших клиентов:

#include "TCPOutConnection.h"

namespace Proxy
{
    TCPOutConnection::TCPOutConnection(asio::io_context& io_context, int port) :
        io_context(&io_context),
        acceptor(io_context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
        clientsMutex(),
        clients()
    {

    }

    TCPOutConnection::~TCPOutConnection()
    {
        Close();
    }

    //Starts listening for clients 
    void TCPOutConnection::Start()
    {
        connect();
    }

    void TCPOutConnection::Close()
    {
        std::lock_guard<std::mutex> guard(closingMutex);
        if (acceptor.is_open())
            acceptor.close();

        {
            std::lock_guard<std::mutex> guard(clientsMutex);
            for (auto c = clients.begin(); c != clients.end(); ++c)
            {
                asio::ip::tcp::socket* socket = *c;
                socket->close();
                delete socket;
            }
            clients.clear();
        }
    }
    //Sends data to each of the clients.
    void TCPOutConnection::Send(std::vector<uint8_t> data)
    {
        if (data.size() == 0)
            return;

        std::lock_guard<std::mutex> guard(clientsMutex);
        for (auto& socket : clients)
        {
            std::vector<uint8_t>* dataToSend = new std::vector<uint8_t>(data.begin(), data.end());
            socket->async_send(asio::buffer(*dataToSend), [socket, this, dataToSend](const asio::error_code& error, std::size_t bytes_transferred) {
                delete dataToSend;
                if (!error)
                    return;

                std::lock_guard<std::mutex> guard(clientsMutex);
                clients.remove(socket);
            });
        }
    }~

    //Still not used. Would be implemented later.
    void TCPOutConnection::OnDataReceived(std::function<void(std::vector<uint8_t>)> callback)
    {

    }

    //Handles connection of new clients. Everytime a client comes, it should listen for another one
    void TCPOutConnection::connect()
    {
        asio::ip::tcp::socket* socket = new asio::ip::tcp::socket(*io_context);
        acceptor.async_accept(*socket, [socket, this](const asio::error_code& error) {
            connect();
            if (error)
            {
                delete socket;
                return;
            }

            asio::ip::tcp::no_delay option1;
            socket->set_option(option1);

            asio::socket_base::receive_buffer_size option2(8192 * 40);
            socket->set_option(option2);

            std::lock_guard<std::mutex> guard(clientsMutex);
            clients.push_back(socket);
        });
    }
}

Хотя вышеприведенное работает нормально, некоторые пакеты теряются, когданаш сервер отправляет слишком много пакетов на прокси (~ 15 пакетов в миллисекунду).

Могу ли я что-нибудь сделать, чтобы не потерять пакеты?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...