Я создаю небольшое приложение для своей компании. Я избавлю вас от деталей, почему, но меня попросили создать прокси-сервер 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 пакетов в миллисекунду).
Могу ли я что-нибудь сделать, чтобы не потерять пакеты?