Каково условие для вызова обработчика завершения basic_stream_socket :: async_write_some? - PullRequest
1 голос
/ 05 августа 2020

В документации для basic_stream_socket::async_write_some указано, что обработчик завершения вызывается «по завершении операции записи». Но что именно это означает? Я могу думать как минимум о двух вещах:

  • , когда ядро ​​(в частности, сетевой стек) берет на себя ответственность за данные
  • , когда сетевой стек на удаленном конце подтвердил (как в TCP ACK) данные, которые были отправлены, и подтверждение достигло локального сетевого стека

1 Ответ

2 голосов
/ 06 августа 2020

Пытался придумать тестовую программу:

Coliru

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

using namespace boost::asio;
using ip::tcp;
using boost::system::error_code;

using namespace std::chrono_literals;
auto now       = &std::chrono::high_resolution_clock::now;
auto sleep_for = [](auto dur) { std::this_thread::sleep_for(dur); };
auto timestamp = [start = now()] { return (now() - start)/1.0ms; };

int main() {
    static constexpr size_t msglen = 16 << 20; // 16 mib
    thread_pool io(1);
    tcp::acceptor a(io, {{}, 7878});
    a.set_option(tcp::acceptor::reuse_address(true));
    a.listen();

#define CHECKED_OPTION(s, name, requested) do { \
    tcp::socket::name option(requested); \
    /*s.set_option(option);*/ \
    s.get_option(option); \
    std::cout << " " << #name << ":" << option.value(); \
} while (0)

    a.async_accept([=](error_code ec, tcp::socket&& con) {
        std::cout << timestamp() << "ms accept " << ec.message();
        std::cout << " " << con.remote_endpoint(ec);

        con.set_option(tcp::no_delay(true));
        CHECKED_OPTION(con, receive_buffer_size, 100);
        CHECKED_OPTION(con, send_buffer_size, 100);

        std::cout << std::endl;

        if (!ec) {
            sleep_for(1s);
            std::cout << timestamp() << "ms start write" << std::endl;
            auto xfr = con.write_some(buffer(std::string(msglen, '*')), ec);
            std::cout << timestamp() << "ms write completed: " << xfr << "/" << msglen << " (" << ec.message() << ")" << std::endl;
        }
    });

    {
        tcp::socket s(io);
        sleep_for(1s);
        std::cout << timestamp() << "ms connecting" << std::endl;
        s.connect({{}, 7878});
        std::cout << timestamp() << "ms connected";
        CHECKED_OPTION(s, receive_buffer_size, 100);
        CHECKED_OPTION(s, send_buffer_size, 100);
        std::cout << std::endl;

        sleep_for(3s);
        std::cout << timestamp() << "ms disconnecting" << std::endl;
    }
    std::cout << timestamp() << "ms disconnected" << std::endl;

    a.cancel();
    io.join();
}

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

Он печатает (на Coliru):

1000.48ms connecting
1001.07ms connected receive_buffer_size:530904 send_buffer_size:1313280
1001.23ms accept Success 127.0.0.1:41614 receive_buffer_size:531000 send_buffer_size:1313280
2001.64ms start write
4001.37ms disconnecting
4001.62ms disconnected
4013.33ms write completed: 4481610/16777216 (Success)

Понятно, что

  • write_some завершается только тогда, когда пакеты подтверждены ACK и возвращено фактическое количество переданных байтов.
  • пакеты ACK ядра независимо от уровня приложения

Фактически пакеты могут поступать не по порядку, и в этом случае ядро ​​подтверждает их индивидуально перед секвенированием, чтобы приложение считало данные через API сокета.

Буферизация

Буферизация неизбежна , но можно настраивать в определенных пределах. Например, раскомментирование этой строки из макроса CHECKED_OPTION:

s.set_option(option); \

дает другой вывод ( Live On Coliru ):

1000.44ms connecting
1001.08ms connected receive_buffer_size:1152 send_buffer_size:2304
1001.31ms accept Success 127.0.0.1:41618 receive_buffer_size:1152 send_buffer_size:2304
2001.88ms start write
4001.42ms disconnecting
4001.61ms disconnected
4008.21ms write completed: 43776/16777216 (Success)
...