2014-09-14 4 views
0

Я пишу синхронный клиент. Его частью является объект Connection, который отвечает за фактическую отправку и получение данных. Вся библиотека написана с использованием класса Boost ASIO ip::tcp::socket.Socket ReceiveTimeout на Linux

У меня есть тест, в котором клиент вызывает метод на сервере (который спит в течение 2 секунд) с тайм-аутом в 1 секунду. Мой код обнаружил, что выполнение заняло больше времени, чем запрошенное, но оно не вернулось вовремя. Вместо этого он возвращался после двух целых секунд.

я сузил проблему до receive метода:

void Connection::receive(const mutable_buffers_1& buffers, const DurationType& timeout) 
{ 
    // to make sure it isn't 0 by mistake 
    auto actualTimeout = std::max(DurationType(milliseconds(1)), timeout); 
    SocketReceiveTimeoutOption timeoutOption(actualTimeout); 
    error_code ec; 
    _socket.set_option(timeoutOption, ec); 
    RPC_LOG(TRACE) << "Setting timeout " << actualTimeout << " returned: " << ec.message(); 
    RPC_LOG(TRACE) << "Receiving..."; 
    if (_socket.receive(buffers, MSG_WAITALL, ec) != buffer_size(buffers)) 
    { 
     throw RpcCommunicationError("Did not receive the expected number of bytes from connection"); 
    } 
    RPC_LOG(TRACE) << "Received! With error code: " << ec.message(); 
} 

DurationType является только удобством ЬурейиМ:

typedef boost::chrono::system_clock ClockType; 
typedef ClockType::time_point::duration DurationType; 

SocketReceiveTimeoutOption вариант реализуется для сокетов:

template <int Name> 
class SocketTimeoutOption 
{ 
public: 
    #ifdef BSII_WINDOWS 
    SocketTimeoutOption(const DurationType& timeout) : _value(static_cast<DWORD>(boost::chrono::duration_cast<boost::chrono::milliseconds>(timeout).count())) {} 
    #else 
    SocketTimeoutOption(const DurationType& timeout) : _value(Utils::toTimeval(timeout)) {} 
    #endif 

    // Get the level of the socket option. 
    template <typename Protocol> 
    int level(const Protocol&) const 
    { 
     return SOL_SOCKET; 
    } 

    // Get the name of the socket option. 
    template <typename Protocol> 
    int name(const Protocol&) const 
    { 
     return Name; 
    } 

    // Get the address of the timeout data. 
    template <typename Protocol> 
    void* data(const Protocol&) 
    { 
     return &_value; 
    } 

    // Get the address of the timeout data. 
    template <typename Protocol> 
    const void* data(const Protocol&) const 
    { 
     return &_value; 
    } 

    // Get the size of the boolean data. 
    template <typename Protocol> 
    std::size_t size(const Protocol&) const 
    { 
     return sizeof(_value); 
    } 

private: 
    #ifdef BSII_WINDOWS 
    DWORD _value; 
    #else 
    timeval _value; 
    #endif 
}; 

typedef SocketTimeoutOption<SO_RCVTIMEO> SocketReceiveTimeoutOption; 
typedef SocketTimeoutOption<SO_SNDTIMEO> SocketSendTimeoutOption; 

И, наконец,

namespace Utils 
{ 
    inline 
     timeval toTimeval(const DurationType& duration) 
    { 
      timeval val; 
      auto seconds = boost::chrono::duration_cast<boost::chrono::seconds>(duration); // TODO: make sure this is truncated down in case there's fractional seconds 
      val.tv_sec = static_cast<long>(seconds.count()); 

      auto micro = boost::chrono::duration_cast<boost::chrono::microseconds>(duration - seconds); 
      val.tv_usec = static_cast<long>(micro.count()); 

      return val; 
    } 
} 

Проблема в том, что, хотя я указываю тайм-аут 1 с, метод receive все еще занимает целые 2 секунды. Вот журнал:

2014-09-14 10: 27: 53.348383 | след | 0x007f24e50ae7c0 | Установка тайм-аута 999917107 наносекунды: Успех
2014-09-14 10: 27: 53.348422 | след | 0x007f24e50ae7c0 | Прием ...
2014-09-14 10: 27: 55.349152 | след | 0x007f24e50ae7c0 | Получено! С кодом ошибки: Успех

Как вы можете видеть, установка тайм-аута работала, но метод receive занял 2 секунды.

Тот же код работает отлично в Windows.

+0

'DurationType' и' SocketReceiveTimeoutOption' не определены. Если они типизированы для фактических типов повышения, пожалуйста, покажите typedefs или используйте фактические типы boost в вашем примере. –

+0

Спасибо. Отредактированный исходный вопрос. Теперь я вижу, что возможно, что у меня ошибка в настройке параметра тайм-аута в случае Linux. Я проверю и обновлю. – Dina

+0

Обновление: я думаю, что преобразование продолжительности в timeval в порядке. Я добавил несколько журналов и получил следующее: Преобразование 999718938 наносекунд в timeval Получено: 0 секунд + 999718 секунд – Dina

ответ

0

socket::receive() будет блокировать до тех пор, как:

  • один или несколько байт данных получено не было успешно
  • ошибка происходит, что бы защитить данные от полученных

Для блокировки синхронных операций, если базовая операция ОС вернется с некритической ошибкой, например, с указанием того, что операция будет блокироваться или должна быть снова проверена, тогда Boost.Asio будет блокироваться в poll(), ожидая файл de scriptor, чтобы стать готовым. Блокирующий вызов на poll() не зависит от опции сокета . После того, как файловый дескриптор готов, Boost.Asio перезагрузит операцию.

Таким образом, сценарий в исходном вопросе происходит следующим образом:

Time | Client         | Server 
-----+----------------------------------------+------------------------------- 
    | socket.connect(...);     | acceptor.accept(...); 
0.00 | socket.set_option(timeout(second(1))); | sleep(seconds(2)); 
0.01 | socket.receive(...);     | 
0.02 | |-- recv(...);       | 
1.02 | | // timeout, errno = EAGAIN   | 
1.03 | |-- poll(socket);      | 
2.00 | | // data available, poll unblocks  | socket.write(...); 
2.01 | `-- recv(...);// success    | 

Чтобы получить желаемое поведение таймаута, либо:

  • Используйте шаблон, представленный в официальном Boost.Asio timeout examples.
  • Вызовите вызов ОС напрямую. Однако будьте осторожны, поскольку другие действия могут косвенно повлиять на этот подход. Например, если асинхронная операция инициируется в сокете, то сокет будет установлен на неблокирующий. Это приведет к немедленному возврату функции recv() на неблокирующее гнездо независимо от опции сокета .
Смежные вопросы