Сбросьте буфер TCP ядра для 'MSG_MORE '-flagged пакеты

отправьте (), страница справочника показывает MSG_MORE флаг, который, как утверждается, действует как TCP_CORK. У меня есть функция обертки вокруг send():

int SocketConnection_Write(SocketConnection *this, void *buf, int len) {
    errno = 0;

    int sent = send(this->fd, buf, len, MSG_NOSIGNAL);

    if (errno == EPIPE || errno == ENOTCONN) {
        throw(exc, &SocketConnection_NotConnectedException);
    } else if (errno == ECONNRESET) {
        throw(exc, &SocketConnection_ConnectionResetException);
    } else if (sent != len) {
        throw(exc, &SocketConnection_LengthMismatchException);
    }

    return sent;
}

Принятие меня хочет использовать буфер ядра, я мог пойти с TCP_CORK, включите каждый раз, когда это необходимо, и затем отключите его для сбрасывания буфера. Но с другой стороны, таким образом, потребность в дополнительном системном вызове возникает. Таким образом, использование MSG_MORE кажется более соответствующим мне. Я просто изменился бы, вышеупомянутое отправляют () строку к:

int sent = send(this->fd, buf, len, MSG_NOSIGNAL | MSG_MORE);

Согласно lwm.net, пакеты будут сброшены автоматически, если они будут достаточно большими:

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

Но этот раздел только относится к TCP_CORK. Теперь, что состоит в том, чтобы сбросить надлежащий путь MSG_MORE пакеты?

Я могу только думать о двух возможностях:

  1. Вызов отправляет () с пустым буфером и без MSG_MORE быть установленным
  2. Повторно примените опцию TCP_CORK, как описано на этой странице

К сожалению, целая тема очень плохо документируется, и я не мог найти много в Интернете.

Я также задаюсь вопросом, как проверить, что все работает как ожидалось? Очевидно, прокручивание сервера strace не опция. Таким образом, самый простой путь состоял бы в том, чтобы использовать netcat и затем посмотрите на strace вывод? Или ядро обработает трафик, переданный по петлевому интерфейсу по-другому?

6
задан user206268 31 March 2010 в 02:02
поделиться

1 ответ

Я взглянул на исходный код ядра и оба предположения кажутся верными. Следующий код извлечен из net / ipv4 / tcp.c (2.6.33.1).

static inline void tcp_push(struct sock *sk, int flags, int mss_now,
                int nonagle)
{
    struct tcp_sock *tp = tcp_sk(sk);

    if (tcp_send_head(sk)) {
        struct sk_buff *skb = tcp_write_queue_tail(sk);
        if (!(flags & MSG_MORE) || forced_push(tp))
            tcp_mark_push(tp, skb);
        tcp_mark_urg(tp, flags, skb);
        __tcp_push_pending_frames(sk, mss_now,
                      (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle);
    }
}

Следовательно, если флаг не установлен , ожидающие кадры обязательно будут сброшены. Но это только в том случае, когда буфер не пуст :

static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset,
             size_t psize, int flags)
{
(...)
    ssize_t copied;
(...)
    copied = 0;

    while (psize > 0) {
(...)
        if (forced_push(tp)) {
            tcp_mark_push(tp, skb);
            __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
        } else if (skb == tcp_send_head(sk))
            tcp_push_one(sk, mss_now);
        continue;

wait_for_sndbuf:
        set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
        if (copied)
            tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);

        if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
            goto do_error;

        mss_now = tcp_send_mss(sk, &size_goal, flags);
    }

out:
    if (copied)
        tcp_push(sk, flags, mss_now, tp->nonagle);
    return copied;

do_error:
    if (copied)
        goto out;
out_err:
    return sk_stream_error(sk, flags, err);
}

Тело цикла while никогда не будет выполнено, потому что psize не больше 0. Затем в разделе out есть еще один шанс, что будет вызвана tcp_push () , но поскольку copied по-прежнему имеет значение по умолчанию, он также завершится ошибкой.

Таким образом, отправка пакета длины 0 никогда не приведет к сбросу.

Следующая теория заключалась в повторном применении TCP_CORK . Давайте сначала посмотрим на код:

static int do_tcp_setsockopt(struct sock *sk, int level,
        int optname, char __user *optval, unsigned int optlen)
{

(...)

    switch (optname) {
(...)

    case TCP_NODELAY:
        if (val) {
            /* TCP_NODELAY is weaker than TCP_CORK, so that
             * this option on corked socket is remembered, but
             * it is not activated until cork is cleared.
             *
             * However, when TCP_NODELAY is set we make
             * an explicit push, which overrides even TCP_CORK
             * for currently queued segments.
             */
            tp->nonagle |= TCP_NAGLE_OFF|TCP_NAGLE_PUSH;
            tcp_push_pending_frames(sk);
        } else {
            tp->nonagle &= ~TCP_NAGLE_OFF;
        }
        break;

    case TCP_CORK:
        /* When set indicates to always queue non-full frames.
         * Later the user clears this option and we transmit
         * any pending partial frames in the queue.  This is
         * meant to be used alongside sendfile() to get properly
         * filled frames when the user (for example) must write
         * out headers with a write() call first and then use
         * sendfile to send out the data parts.
         *
         * TCP_CORK can be set together with TCP_NODELAY and it is
         * stronger than TCP_NODELAY.
         */
        if (val) {
            tp->nonagle |= TCP_NAGLE_CORK;
        } else {
            tp->nonagle &= ~TCP_NAGLE_CORK;
            if (tp->nonagle&TCP_NAGLE_OFF)
                tp->nonagle |= TCP_NAGLE_PUSH;
            tcp_push_pending_frames(sk);
        }
        break;
(...)

Как видите, есть два способа сбросить данные. Вы можете установить TCP_NODELAY на 1 или TCP_CORK на 0. К счастью, оба не будут проверять, установлен ли уже флаг. Таким образом, мой первоначальный план повторного применения флага TCP_CORK можно оптимизировать, чтобы просто отключить его, даже если он в настоящее время не установлен.

Надеюсь, это поможет кому-нибудь с подобными проблемами.

11
ответ дан 8 December 2019 в 17:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: