wsasend lpnumberofbytesSent

Я использую wsasend на структурированном сервере IOCP. Есть одна проблема.

wsabuf [bufcount - 1] .buf = pPacket-> GetPacketBufferPtr ();
wsabuf [bufcount - 1] .len = (int) pPacket-> Get_PacketSize ();
iSendSize + = wsabuf [bufcount - 1] .len;
bufcount ++;
int retval = WSASend (pSession-> socket, wsabuf, bufcount-1, & sendbytes,flag, & pSession-> overlapped_Send, NULL);
if (retval == SOCKET_ERROR)
{

    if (WSAGetLastError ()! = WSA_IO_PENDING)
    {
      ......
    }
}
if (retval == 0)
{
    if (sendbytes! = iSendSize)
    {
       ........
    }
}
.....

В приведенном выше коде я сохраняю пакет для отправки в wsabuf и отправляю его через wsasend. И, наконец, я сравнил sendbytes и iSendSize. Кстати, sendbytes и iSendSize разные. Я не знаю почему.


person NamHyuk Im    schedule 20.12.2017    source источник
comment
Из документации: используйте NULL для этого параметра ( lpNumberOfBytesSent), если параметр lpOverlapped не равен NULL, чтобы избежать потенциально ошибочных результатов.   -  person Igor Tandetnik    schedule 20.12.2017
comment
если вы используете асинхронный ввод-вывод, вы не должны использовать (игнорировать) sendbytes, но overlapped_Send.InternalHigh или NumberOfBytesTransferred, возвращаемые GetOverlappedResult (это действительно просто копирует значение InternalHigh сюда), когда операция завершена   -  person RbMm    schedule 20.12.2017
comment
@IgorTandetnik Документация неверна. В Windows XP с пакетом обновления 2 (SP2) это приведет к сбою с нарушением прав доступа, так как нет проверки, является ли lpNumberOfBytesSent NULL или нет. WSASend работает иначе в Windows XP. Он не игнорирует lpNumberOfBytesSent, даже если он был установлен в NULL и функция была вызвана как перекрывающаяся.   -  person a man in love    schedule 23.09.2019


Ответы (1)


фактическое количество переданных байтов, возвращаемое драйвером только после завершения операции. io скопируйте это значение в IO_STATUS_BLOCK.Information передается операции ввода-вывода. в результате пользователь возвращает это значение. но, конечно, только после завершения операции.

win32 API используйте OVERLAPPED вместо IO_STATUS_BLOCK — переинтерпретировать приведение OVERLAPPED к IO_STATUS_BLOCK и передать этот указатель ядру. поэтому InternalHigh будет содержать фактическое количество переданных байтов, но только после завершения операции (в случае возврата ошибки синхронного - подсистема io не заполнит это поле, поэтому его значение не определено при ошибке. по смыслу, конечно, 0).

WSASend получить значение (после обращения к ядру) из OVERLAPPED.InternalHigh и если lpNumberOfBytesSent не 0 - скопировать сюда. если вы используете синхронный дескриптор сокета - в этот момент операция ввода-вывода уже будет завершена (внутреннее ожидание этого подсистемы ввода-вывода, прежде чем вернуться к вызывающей стороне), и действительное значение из OVERLAPPED.InternalHigh будет скопировано в *lpNumberOfBytesSent

в коде это будет выглядеть

if (!lpOverlapped)
{
    OVERLAPPED Overlapped = {};
    lpOverlapped = &Overlapped;
}

ZwDeviceIoControlFile(.. reinterpret_cast<IO_STATUS_BLOCK*>(lpOverlapped) ..)

if (lpNumberOfBytesSent)
{
  *lpNumberOfBytesSent = (ULONG)lpOverlapped->InternalHigh;
}

в случае обработки асинхронного сокета операция обычно еще не завершена после возврата из ядра. в результате lpOverlapped->InternalHigh еще не заполнено правильными номерами байтов. и

*lpNumberOfBytesSent = (ULONG)lpOverlapped->InternalHigh;

получил неверный (неопределенный, если вы и система не инициализируете его, скажем, 0) результат.

вывод - нельзя использовать sendbytes для асинхронной операции ввода-вывода. что здесь не определено. вы можете и должны получить это значение когда ввод-вывод завершен. то, как вы его получили, уже зависит от того, как вы уведомили о завершении.

  • если вы используете BindIoCompletionCallback - вы получили его в FileIOCompletionRoutine в аргументе dwNumberOfBytesTransfered
  • если вы используете CreateThreadpoolIo- вы получили это в IoCompletionCallback в аргументе NumberOfBytesTransferred
  • если вы используете собственный IOCP и GetQueuedCompletionStatus — вы получили обратный указатель на ваш lpOverlapped, используемый при вызове WSASend (или какой-то другой функции ввода-вывода — это уже ваша задача определить, где этот lpOverlapped используется ) после завершения операции. на этом этапе вы можете вызвать GetOverlappedResult для этого lpOverlapped (bWait вы можете установить любое значение — не имеет значения, потому что операция уже завершена — в любом случае API вернется немедленно, без ожидания) и вы получили фактическое количество переданных байтов в lpNumberOfBytesTransferred. однако GetOverlappedResult просто скопируйте значение lpOverlapped->InternalHigh в *lpNumberOfBytesTransferred, чтобы вы могли и направить, сами используйте InternalHigh без вызова GetOverlappedResult
person RbMm    schedule 20.12.2017