#c #boost-asio
#c #boost-asio
Вопрос:
Я пишу простой прокси-сервер, который анализирует пакеты и отправляет их на другой экземпляр сервера, например, что-то вроде этого:
клиент -> MyProxy -> SQLServer ->
клиент <- MyProxy <- SQLServer <-
Он должен выполняться в бесконечном цикле. Моя проблема сейчас в том, что прокси, похоже, теряет пакеты, иногда он даже зависает. Когда я добавляю много отладочной информации (которая записывается в консоль), прокси-сервер работает намного стабильнее. Кажется, что прокси-сервер работает слишком быстро .. 🙂
Я почти уверен, что делаю что-то неправильно, вот код моего класса session (код получен из примеров Boost:: Asio).
#include "session.h"
#include <iostream>
using namespace std;
session::session(boost::asio::io_serviceamp; io_service)
: socket_(io_service)
, sqlsocket_(io_service)
, io_service_(io_service)
, resolver(io_service)
{
cout << "session::session()" << endl;
}
session::~session()
{
cout << "session::~session()" << endl;
cout << "closing session ..." << endl;
}
tcp::socketamp; session::socket()
{
return socket_;
}
void session::start()
{
cout << "session::start()" << endl;
cout << "starting session ..." << endl;
// connect to the sqlserver database
tcp::resolver::query query("192.168.1.50", "1317");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::endpoint endpoint = *endpoint_iterator;
sqlsocket_.async_connect(endpoint,
boost::bind(amp;session::handle_sqlserver_connect, this,
boost::asio::placeholders::error, endpoint_iterator));
// TODO: connect to the connector
}
void session::handle_read(const boost::system::error_codeamp; error,
size_t bytes_transferred)
{
cout << "session::handle_read()" << endl;
if (!error)
{
cout << "session::handle_read() (read: "
<< bytes_transferred << ")"
<< endl;
boost::asio::async_write(sqlsocket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(amp;session::handle_sqlserver_write, this,
boost::asio::placeholders::error, bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_read(const boost::system::error_codeamp; error,
size_t bytes_transferred)
{
cout << "session::handle_sqlserver_read()" << endl;
if (!error)
{
cout << "session::handle_sqlserver_read() (read: "
<< bytes_transferred << ")"
<< endl;
boost::asio::async_write(socket_,
boost::asio::buffer(data_, bytes_transferred),
boost::bind(amp;session::handle_write, this,
boost::asio::placeholders::error, bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_write(const boost::system::error_codeamp; error,
size_t bytes_transferred)
{
static int count = 0;
cout << count << ". session::handle_write()" << endl;
if (!error)
{
cout << "session::handle_write() (read: "
<< bytes_transferred << ")"
<< endl;
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(amp;session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_write(const boost::system::error_codeamp; error,
size_t bytes_transferred)
{
cout << "session::handle_sqlserver_write()" << endl;
if (!error)
{
cout << "session::handle_sqlserver_write() (read: "
<< bytes_transferred << ")"
<< endl;
sqlsocket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(amp;session::handle_sqlserver_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
delete this;
}
}
void session::handle_sqlserver_connect(const boost::system::error_codeamp; error,
tcp::resolver::iterator endpoint_iterator)
{
cout << "session::handle_sqlserver_connect()" << endl;
if (!error)
{
socket_.async_read_some(boost::asio::buffer(data_, max_length),
boost::bind(amp;session::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else if (endpoint_iterator != tcp::resolver::iterator())
{
sqlsocket_.close();
tcp::endpoint endpoint = *endpoint_iterator;
sqlsocket_.async_connect(endpoint,
boost::bind(amp;session::handle_sqlserver_connect, this,
boost::asio::placeholders::error, endpoint_iterator));
}
}
Нужно ли мне использовать другие методы вместо async_ * для моего типа прокси?
Я переношу код из какого-то старого проекта, который моя компания хочет перезапустить снова, но с boost вместо материала Winsock, который использовался ранее.
Есть идеи, в чем может быть проблема?
Старый код делал что-то вроде этого: основной метод с вызовом метода accept создавал два потока
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatclient, (LPVOID)cs, 0, 0);
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenatserver, (LPVOID)cs, 0, 0);
и потоки вызвали следующие функции:
void listenatclient(LPVOID connection)
{
connection_s* cs = (connection_s*)connection;
char inMessagecli[MSG_SIZE];
int rcount = 0;
...
do
{
memset(inMessagecli, 0, MSG_SIZE);
rcount = recv((SOCKET)cs->client, inMessagecli, MSG_SIZE, 0);
if (rcount != SOCKET_ERROR)
{
// analyze package
...
send((SOCKET)cs->server, inMessagecli, rcount, 0);
}
} while (rcount > 0);
}
void listenatserver(LPVOID connection)
{
connection_s* cs = (connection_s*)connection;
char inMessageserv[MSG_SIZE];
int rcount = 0;
do
{
memset(inMessageserv, 0, MSG_SIZE);
rcount = recv((SOCKET)cs->server, inMessageserv, MSG_SIZE, 0);
if (rcount != SOCKET_ERROR)
{
send((SOCKET)cs->client, inMessageserv, rcount, 0);
}
} while (rcount > 0);
}
[ПРАВИТЬ]:
Я пытался запустить команды async_read для клиента и sqlserver одновременно, но теперь я постоянно получаю сбои, иногда в boost:: bind, иногда в других частях библиотеки boost.
Кажется, что происходит то, что создаются 2 или три соединения (3 сеанса). При закрытии первого сеанса сбой, похоже, происходит во втором сеансе.
Является ли boost asio небезопасным для пользователя или я делаю здесь что-то ужасно неправильное :-)?
Я разместил код для маленького прокси-сервера здесь:
session.h : ссылка
session.cpp : ссылка
server.h: ссылка
server.cpp : ссылка
ProxyServer.cpp : ссылка
Комментарии:
1. Почему вы смешиваете и сопоставляете
recv()
иsend()
вызовы с ASIO? Я думаю, вам нужно изменить обработку ошибок для recv (2), чтобы она проверяла успешность, а затем предполагала сбой, а не предполагала, что единственным кодом возврата являетсяSOCKET_ERROR
.2. @Sean Я не думаю, что @user их смешивает, код, использующий
send
иrecv
, — это какой-то старый код winsock.3. Да, код Winsock — это просто старый проект, показывающий, как это делалось раньше, код Boost — это новый проект, поэтому я не смешиваю два вызова API.
4. @Сэм Миллер: Данные сеанса — это данные boost::array<char, 8192>_;
Ответ №1:
Я подозреваю, что происходит то, что один из ваших вызовов async_read_some возвращает «некоторые» данные, но недостаточно для того, чтобы сервер SQL был удовлетворен тем, что он получил полный запрос. Ваш код всегда следует по пути read_from_client -> send_to_server -> read_from_server -> send_to_client. Он не обрабатывает случай, когда вам нужен read_from_client -> send_to_server — > read_from_client -> send_to_server -> read_from_server -> send_to_client.
Код, который вы написали до сих пор, не выполняет те же операции, что и исходный. В частности, старый код одновременно прослушивал чтения в обоих сокетах. К счастью, поскольку вы используете ASIO, вам не нужно возиться с потоками. Просто выдайте одновременные запросы async_read_some на оба сокета и обрабатывайте их асинхронно.
Комментарии:
1. 1 Мне только что пришлось проделать то же самое с async_read_some(). При реализации какого-либо процессора запросов вы должны предоставить некоторую обработку в handle_read(), чтобы объединить одно или несколько операций чтения в полные запросы.
2. Спасибо за ответ, я попытаюсь проверить, есть ли еще данные для чтения из сокета, и, если да, обработаю их в своих методах обратного вызова read.