Перекрытый обратный вызов WSARecv () не вызывается в приложении MFC - PullRequest
1 голос
/ 25 октября 2010

У меня есть COM-компонент, реализованный на C ++ с ATL, который использует перекрывающийся сокет ввода-вывода. Сразу после установления соединения с сервером он запускает перекрывающееся чтение в сокете с кодом, подобным следующему:

// Pass pointer to this instance as hEvent parameter, for use by callback
m_recvOverlapped.hEvent = reinterpret_cast<HANDLE>(this);

int rc = ::WSARecv(m_s, &wsabuf, 1, &m_recvNumberOfBytes, &m_recvFlags, &m_recvOverlapped, RecvCallback);
if (rc == SOCKET_ERROR)
{
    // If error is WSA_IO_PENDING, then the I/O is still in progress.  Otherwise, something bad happened.
    int error = ::WSAGetLastError();
    if (error != WSA_IO_PENDING)
    {
        ReceiveError(error);
    }
}

И у меня есть функция обратного вызова, которая выглядит примерно так:

void CALLBACK CMySocket::RecvCallback(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags)
{
CMySocket* socket = reinterpret_cast<CMySocket*>(lpOverlapped->hEvent);
ATLASSERT(socket != 0);
if (!socket)
    return;

socket->ReceiveCompleted(dwError, cbTransferred, lpOverlapped, dwFlags);
}

Этот компонент COM отлично работает в модульных тестах, при использовании в приложении командной строки и при использовании в приложении .NET GUI (через COM-взаимодействие). Однако когда я использую этот компонент в приложении MFC, RecvCallback никогда не вызывается, когда сервер отправляет ему данные.

WSARecv() возвращает SOCKET_ERROR, а WSAGetLastError() возвращает WSA_IO_PENDING, как и ожидалось для асинхронных перекрывающихся операций чтения.

Когда я использую приложение SysInternals TcpView для наблюдения за происходящим, это указывает на то, что клиент получает данные. Но обратный вызов никогда не вызывается.

Отправка данных на сервер через подключенный сокет работает нормально.

Я звоню CoInitializeEx() и WSAStartup() в методе InitInstance() моего приложения MFC.

Есть идеи?

Ответы [ 2 ]

1 голос
/ 25 октября 2010

Да, это действительно так.APC обрабатываются только тогда, когда поток переходит в «ожидаемое» состояние ожидания - вызывает либо функцию SleepEx, либо WaitForMultipleObjectsEx, либо MsgWaitForMultipleObjectsEx.

Единственное, что я хотел бы исправить, это использование * 1006.Член hEvent * как заполнитель для ваших "пользовательских" данных - плохая идея.Потому что ОС попытается установить это «событие» после завершения ввода-вывода.

Распространенным способом передачи некоторой «пользовательской» информации в маршрутизацию обратного вызова фактически является использование пользовательской структуры, которая заменяет OVERLAPPED, добавляя больше участников по мере необходимости (он же OVERLAPPED_PLUS).Тогда ваша маршрутизация обратного вызова может привести OVERLAPPED к вашему OVERLAPPED_PLUS, там вы увидите всех участников.

Еще один момент: поскольку вы пишете COM-объект - у вас, вероятно, нет возможностинаписать свой собственный цикл сообщений, поэтому может быть сложно гарантировать вход в ожидаемое оповещение.

0 голосов
/ 25 октября 2010

ОК, я нашел ответ, выполнив поиск других вопросов о переполнении стека, касающихся WSARecv.

Из ответа Лена Холгейта на Перекрытый ввод-вывод Win32 - подпрограммы завершения или WaitForMultipleObjects? :

. , , Вы можете пройти процедуру завершения, которая вызывается, когда происходит завершение. Это известно как «тревожный ввод-вывод» и требует, чтобы поток, который выполнил вызов WSARecv (), находился в состоянии «оповещения» для вызова процедуры завершения. Потоки могут переводить себя в состояние тревоги несколькими способами (вызывая SleepEx () или различные EX-функции функций Wait и т. Д.). , , .

Оказывается, что если я запускаю таймер в приложении MFC, которое периодически вызывает SleepEx(0, TRUE), то вызывается обратный вызов приема. Итак, проблема в том, что основной поток MFC, который вызывает метод моего COM-объекта, который вызывает WSARecv(), никогда не переходит в состояние оповещения самостоятельно.

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

...