2010-01-11 6 views
22

У меня очень простой тест производительности сервера/клиента с использованием boost :: asio в Windows, и, похоже, он работает очень плохо. Я надеюсь, что я просто неправильно использую библиотеку и буду благодарен за любые советы.Плохая производительность boost.ASIO

У меня есть класс сеанса, который пишет длину сообщения, а затем записывает сообщение, а затем ждет, чтобы прочитать длину сообщения, а затем прочитать сообщение и продолжает делать это снова и снова без перерыва. Однако, когда я запускаю его локально на своем собственном компьютере, я получаю невероятно высокую производительность; когда я запускаю сервер на одном компьютере и клиент на другом компьютере, даже в той же сети, производительность замедляется, и для операции чтения/записи требуется целых 1 секунда.

Файл исходного кода сервера выглядит следующим образом:

#include <cstdlib> 
#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 

using namespace boost; 
using namespace boost::asio; 
using namespace boost::asio::ip; 
using namespace std; 

class Session { 
    public: 

    Session(io_service& ioService) 
     : m_socket(ioService) {} 

    tcp::socket& GetSocket() { 
     return m_socket; 
    } 

    void StartRead() { 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)), 
     bind(&Session::HandleSizeRead, this, placeholders::error, 
     placeholders::bytes_transferred)); 
    } 

    void StartWrite(const char* message, int messageSize) { 
     m_messageSize = messageSize; 
     m_message = new char[m_messageSize]; 
     memcpy(m_message, message, m_messageSize); 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
     bind(&Session::HandleSizeWritten, this, placeholders::error)); 
    } 

    void HandleSizeRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     m_message = new char[m_messageSize]; 
     async_read(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageRead, this, placeholders::error, 
      placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     cout << string(m_message, m_messageSize) << endl; 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
      bind(&Session::HandleSizeWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleSizeWritten(const system::error_code& error) { 
     if(!error) { 
     async_write(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageWritten(const system::error_code& error) { 
     if(!error) { 
     delete m_message; 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, 
      sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this, 
      placeholders::error, placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    private: 
    tcp::socket m_socket; 
    int m_messageSize; 
    char* m_messageSizeIterator; 
    char* m_message; 
}; 

class Server { 
    public: 

    Server(io_service& ioService, short port) 
     : m_ioService(ioService), 
      m_acceptor(ioService, tcp::endpoint(tcp::v4(), port)) { 
     Session* new_session = new Session(m_ioService); 
     m_acceptor.async_accept(new_session->GetSocket(), bind(&Server::HandleAccept, 
     this, new_session,asio::placeholders::error)); 
    } 

    void HandleAccept(Session* new_session, const system::error_code& error) { 
     if(!error) { 
     new_session->StartRead(); 
     new_session = new Session(m_ioService); 
     m_acceptor.async_accept(new_session->GetSocket(), bind(
      &Server::HandleAccept, this, new_session, placeholders::error)); 
     } else { 
     delete new_session; 
     } 
    } 

    private: 
    io_service& m_ioService; 
    tcp::acceptor m_acceptor; 
}; 

int main(int argc, char* argv[]) { 
    try { 
    if(argc != 2) { 
     cerr << "Usage: server <port>\n"; 
     return 1; 
    } 
    io_service io_service; 
    Server s(io_service, atoi(argv[1])); 
    io_service.run(); 
    } catch(std::exception& e) { 
    cerr << "Exception: " << e.what() << "\n"; 
    } 
    return 0; 
} 

И клиентский код выглядит следующим образом:

#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <boost/bind.hpp> 
#include <boost/asio.hpp> 

using namespace boost; 
using namespace boost::asio; 
using namespace boost::asio::ip; 
using namespace std; 

class Session { 
    public: 

    Session(io_service& ioService) 
     : m_socket(ioService) {} 

    tcp::socket& GetSocket() { 
     return m_socket; 
    } 

    void StartRead() { 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)), 
     bind(&Session::HandleSizeRead, this, placeholders::error, 
     placeholders::bytes_transferred)); 
    } 

    void StartWrite(const char* message, int messageSize) { 
     m_messageSize = messageSize; 
     m_message = new char[m_messageSize]; 
     memcpy(m_message, message, m_messageSize); 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
     bind(&Session::HandleSizeWritten, this, placeholders::error)); 
    } 

    void HandleSizeRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     m_message = new char[m_messageSize]; 
     async_read(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageRead, this, placeholders::error, 
      placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     cout << string(m_message, m_messageSize) << endl; 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
      bind(&Session::HandleSizeWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleSizeWritten(const system::error_code& error) { 
     if(!error) { 
     async_write(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageWritten(const system::error_code& error) { 
     if(!error) { 
     delete m_message; 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, 
      sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this, 
      placeholders::error, placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    private: 
    tcp::socket m_socket; 
    int m_messageSize; 
    char* m_messageSizeIterator; 
    char* m_message; 
}; 

int main(int argc, char* argv[]) { 
    try { 
    if(argc != 3) { 
     cerr << "Usage: client <host> <port>\n"; 
     return 1; 
    } 
    io_service io_service; 
    tcp::resolver resolver(io_service); 
    tcp::resolver::query query(tcp::v4(), argv[1], argv[2]); 
    tcp::resolver::iterator iterator = resolver.resolve(query); 
    Session session(io_service); 
    tcp::socket& s = session.GetSocket(); 
    s.connect(*iterator); 
    cout << "Enter message: "; 
    const int MAX_LENGTH = 1024; 
    char request[MAX_LENGTH]; 
    cin.getline(request, MAX_LENGTH); 
    int requestLength = strlen(request); 
    session.StartWrite(request, requestLength); 
    io_service.run(); 
    } catch (std::exception& e) { 
    cerr << "Exception: " << e.what() << "\n"; 
    } 
    return 0; 
} 

Любая помощь будет оценена, спасибо.


Для моих целей, посылая действительно очень маленькие сообщения и желая виртуальные ответы в режиме реального времени, отключение алгоритма Нэгла оказался причиной плохой работы.

+0

Вы исключили возможности того, что может сделать ваш маршрутизатор, что вызывает проблему? Каково использование ЦП, как на каждой машине? – bobber205

+0

Использование ЦП на обеих машинах равно 0. Что касается маршрутизатора, являющегося проблемой, я написал аналогичную программу без использования ASIO, и она работала очень быстро. – Kranar

+3

Начните с тестирования вашей ссылки с помощью iperf. Затем инструмент весь процесс - создан ли сокет? Успешно ли связывание? Помогло ли решение? Работало ли подключение к серверу? Работала первая отправка? Работает ли первая работа? Вызывает ли какой-либо вызов API сети какие-либо ошибки? Что-нибудь дольше, чем ожидалось? Посмотрите на сетевой трафик. Есть ли межсетевой экран на сервере? Включен ли алгоритм Nagle? Серверу потребовалось много времени, чтобы ответить? Есть ли какая-то задержка, которую вы не ожидали в несетевом коде в клиенте? – Permaquid

ответ

36

Вы должны отключить Nagle algorithm. Вызов:

m_socket.set_option(tcp::no_delay(true)); 

В случае необходимости для вашего кода.

+0

+1 для предложения и для ссылки в nagle на wiki. Однако - хотелось бы видеть предупреждение о том, что поворот Нагле, вероятно, снизит общую пропускную способность. – quixver

+2

@quixver Нет nagle приведет к большему количеству пакетов в сети, если вы отправляете байты с небольшим промежутком между ними. Таймер (например, Nagle) для объединения данных приведет к уменьшению количества пакетов и, таким образом, повышению общей пропускной способности сети. Это справедливо для интерактивного трафика клавиатуры (например, telnet, ssh) и было большой частью трафика Ethernet двадцать лет назад. Для связи между программами Нагле приводит к более низкой пропускной способности (как в случае исходного вопроса), а не к более высокой пропускной способности. См., Например, что все сообщение было передано async_write(), и поэтому нет необходимости ждать отправки. – janm

6

Для моих целей отправка действительно действительно маленьких сообщений и получение виртуальных ответов в реальном времени, отключение алгоритма Нагле оказалось причиной плохой работы.

Смежные вопросы