Медленная производительность возникает при копировании данных на сервер TCP с помощью Windows API sockets

В этой статье данная статья предоставляет обходные пути решения проблемы, при которой медленная производительность возникает при копировании данных на сервер TCP с помощью Windows API sockets.

Применяется к:   Windows Server 2012 R2, Windows 10 — все выпуски
Исходный номер КБ:   823764

Симптомы

При запуске программы, использующей API Windows sockets, при копировании данных на TCP-сервере может возникнуть медленная производительность.

Если сделать трассировку сети с помощью сетевого обнюхиватель, например Microsoft Network Monitor, сервер TCP отправляет сегмент TCP ACK в последний сегмент TCP в потоке данных TCP в отложенном отложенном времени подтверждения (также известном как отложенный ACK-отоператор). По умолчанию для Windows операционных систем значение этого времени составляет 200 миллисекунд (ms). Типичный поток данных для отправки данных в 64 килобайта (КБ) похож на следующую последовательность:

Клиент->Server 1460 bytes
Клиент->Server 1460 bytes
Сервер->ACK клиента
Клиент->Server 1460 bytes
Клиент->Server 1460 bytes
Сервер->ACK клиента
....
Клиент->Server 1460 bytes
Клиент->Server 1460 bytes
Сервер->клиент ACK-PUSH
Bytes client->Server 1296
-> ACK 200 ms

Причина

Эта проблема возникает из-за архитектурного поведения API Windows sockets и afd.sys. Эта проблема возникает, если все следующие условия верны:

  • Программа Windows sockets использует неблокирующие розетки.

  • Один вызов отправки или вызов WSASend заполняет весь буфер отправки в основном розетке.

    Например, программа использует функцию Windows sockets для изменения буфера отправки розетки по умолчанию до 32 КБ во время процедур инициализации setsockopt розетки:

    setsockopt( sock, SOL_SOCKET, 32768, (char *) &val, sizeof( int ));
    

    Позже, когда программа отправляет данные, она отправляет вызов отправки или вызов WSASend и отправляет 64 КБ данных во время каждого отправки:

    send(socket, pWrBuffer, 65536, 0);
    

    В этом сценарии при каждом вызове 64 КБ данных программа возвращает код ошибки SOCKET_ERROR при заполнении буфера розетки 32-КБ. После вызова функции WSAGetLastError программа получает код ошибки WSAEWOULDBLOCK. Большинство программ используют функцию выбора Windows для проверки состояния розетки. В этом сценарии функция выбора не сообщает о том, что розетка является writable, пока клиент не получит выдающийся сегмент TCP ACK. По умолчанию в Windows среде это может занять до 200 мс из-за задержки алгоритма подтверждения.

  • Удаленный TCP-сервер подтверждает все сегменты TCP перед отправкой клиентом последнего сегмента TCP с набором push-бита.

Обходной путь

Чтобы решить эту проблему, используйте любой из следующих методов.

Метод 1. Использование блокирующих розеток

Эта проблема возникает только с незаблокирующимися розетками. При использовании блокирующего розетки эта проблема не возникает, так как afd.sys по-разному обрабатывает буфер розетки. Дополнительные сведения о блокировке и неблокировке программирования розетки см. в документации microsoft Platform SDK.

Метод 2. Сделайте размер буфера отправки для розетки больше размера буфера, чем размер буфера отправки программы

Чтобы изменить буфер отправки розетки, используйте функцию Windows sockets для определения текущего размера буфера отправки (SO_SNDBUF), а затем используйте функцию для определения размера буфера отправки getsockopt setsockopt розетки. По завершению значение SO_SNDBUF должно быть не менее чем на 1 byte больше размера буфера отправки программы.

Измените вызов отправки или вызов WSASend, чтобы указать размер буфера не менее чем на 1 byte меньше SO_SNDBUF значения. В более ранних примерах в разделе "Причина" этой статьи можно изменить вызов setockopt на следующее значение,

setsockopt( sock, SOL_SOCKET, 65537, (char *) &val, sizeof( int ));

или вы можете изменить отправку вызова на следующее значение:

send(socket, pWrBuffer, 32767, 0);

Вы также можете использовать любое сочетание этих значений.

Метод 3. Изменение параметров TCP/IP на сервере TCP

Важно!

В этот раздел, описание метода или задачи включены действия, содержащие указания по изменению параметров реестра. Однако неправильное изменение параметров реестра может привести к возникновению серьезных проблем. Поэтому следует в точности выполнять приведенные инструкции. Для дополнительной защиты создайте резервную копию реестра, прежде чем редактировать его. Так вы сможете восстановить реестр, если возникнет проблема. Дополнительные сведения о том, как создать и восстановить реестр, щелкните следующий номер статьи, чтобы просмотреть статью в базе знаний Майкрософт:
322756 Создание резервной копии и восстановление реестра Windows

Измените параметры TCP/IP на сервере TCP, чтобы сразу же подтвердить входящие сегменты TCP. Это решение лучше всего работает в среде с большой клиентской базой установки и в которой невозможно изменить поведение программы. Для сценариев, в которых удаленный сервер TCP работает на Windows сервере, необходимо изменить реестр удаленного сервера. В других операционных системах см. документацию операционной системы для получения сведений об изменении отложенного времени подтверждения.

На сервере, который Windows 2000, выполните следующие действия:

  1. Начните редактор реестра (Regedit.exe).
  2. Найдите и нажмите следующий подкай реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. В меню Редактирование нажмите кнопку Добавить значение, а затем создайте следующее значение реестра:
    Имя значения: TcpDelAckTicks
    Тип данных: REG_DWORD
    Данные значения: 0
  4. Quit Registry Editor.
  5. Перезапустите Windows, чтобы это изменение вступает в силу.

На сервере, на Windows XP или Windows Server 2003, выполните следующие действия:

  1. Начните редактор реестра.
  2. Найдите и нажмите следующий подкай реестра: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\<Interface GUID>
  3. В меню Правка выберите пункт Создать, а затем Параметр DWORD.
  4. Назови новое значение TcpAckFrequency и назначьте ему значение 1.
  5. Quit Registry Editor.
  6. Перезапустите Windows, чтобы это изменение вступает в силу.

Метод 4. Изменение поведения буферизации в afd.sys для неблокирующих розеток

Важно!

В этот раздел, описание метода или задачи включены действия, содержащие указания по изменению параметров реестра. Однако неправильное изменение параметров реестра может привести к возникновению серьезных проблем. Поэтому следует в точности выполнять приведенные инструкции. Для дополнительной защиты создайте резервную копию реестра, прежде чем редактировать его. Так вы сможете восстановить реестр, если возникнет проблема. Дополнительные сведения о том, как создать и восстановить реестр, щелкните следующий номер статьи, чтобы просмотреть статью в базе знаний Майкрософт: 322756 Как создать и восстановить реестр в Windows

Примечание

Этот ключ реестра доступен только для Windows Server 2003 с Пакет обновления 1 и последующими пакетами служб.

  1. Нажмите кнопку Начните, введитеregedit.exe, а затем нажмите кнопку ОК.
  2. Найдите и откройте следующий подраздел реестра:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\AFD\Parameters
  3. В меню Правка выберите пункт Создать, а затем Параметр DWORD.
  4. Назови новое значение NonBlockingSendSpecialBuffering и назначьте ему значение 1.
  5. Редактор реестра exit.
  6. Перезапустите Windows, чтобы это изменение вступает в силу.

Статус

Корпорация Майкрософт подтвердила, что это проблема в продуктах Майкрософт, перечисленных в разделе "Применяется к".

Ссылки

328890 Новая запись реестра для управления поведением TCP Acknowledgment (ACK) в Windows XP и Windows Server 2003