Что стоит за проблемами утверждения CAsyncSocket и ошибками «неправильного аргумента» в моем коде сокетов MFC? - PullRequest
5 голосов
/ 29 апреля 2009

Меня попросили посмотреть код для друга. (Я справедливо колебался из-за MFC и множества плохого кода, но он победил ...)

Это приложение на основе диалогового окна, которое использует CAsyncSocket.

Проблема проявляется в некоторых нон-стоп отладочных и других подобных вещах - также есть проблема с макросом MFC ENSURE() - проверка сокета на нулевое значение. Все проблемы происходят глубоко в MFC.

Некоторое прибегание к поиску ресурсов показало возможные утечки ресурсов, если использовать темы в Vista / XP, но я не думаю, что здесь проблема.

Код довольно плохой из-за моих нескольких часов отладки, но в основном он делает следующее:

(Когда соединение установлено, проблем нет - это только в случае отсутствия соединения)

  • Calls Connect (сервер, сокет) (на производном CAsyncSocket объекте)
  • В OnConnect() нам сообщают, что соединение не работает / не подключено.
  • Внутри окна таймера для основного диалога / приложения есть таймер. Когда вызывается событие / обработчик таймера, мы проверяем, подключено ли.
  • Если мы обнаружили, что мы не подключены (OnConnect() был не в порядке), мы вызываем CAsyncSocket::Close(), затем вызываем CAsyncSocket::Create() (без параметров), затем вызываем CAsyncSocket::Connect(server, port)

Обратите внимание, что первоначальный вызов Connect() не имел предшествующего вызова Create().

Мой первый настоящий вопрос:

  • В чем разница между ними и зачем нужен Create()? (если я удаляю его, он больше не падает, но я также не подключаюсь при восстановлении соединения)

Общий вопрос:

  • Что именно не так с дизайном кода выше?
  • Как это вообще должно работать?

EDIT:

Я исправил код так, чтобы все пути проходили через вызовы Create() затем Connect().

У меня все еще есть проблема с утверждением в CAsyncSocket::DoCallBack() - последняя строка кода ниже утверждает:

void PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
    if (wParam == 0 && lParam == 0)
        return;

    // Has the socket be closed - lookup in dead handle list
    CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

    // If yes ignore message
    if (pSocket != NULL)
        return;

    pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, FALSE);
    if (pSocket == NULL)
    {
        // Must be in the middle of an Accept call
        pSocket = CAsyncSocket::LookupHandle(INVALID_SOCKET, FALSE);
        ENSURE(pSocket != NULL);

Если я прохожу через это, то получаю сообщение: «Обнаружен неправильный аргумент»

Я думаю (но не уверен), что MFC пытается перезвонить сокету ПОСЛЕ того, как я его закрыл. Он находится в методе обратного вызова (DoCallback()), но я уже вызвал Close() в сокете.

Так что это похоже на проблему с MFC, если только я не должен сначала отписаться.

Ответы [ 2 ]

6 голосов
/ 29 апреля 2009

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

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

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

Возможно, если бы вы потратили время на то, чтобы понять модель MFC, вы бы получили момент «АГА» и лучше его поняли. Я не фанат Winsock - я больше привык к миру UNIX, где синхронизация была подходящим способом, и вы просто запускали отдельные процессы / потоки, если вам нужно асинхронное поведение.

CAsyncSocket, я подозреваю, все еще испытывает затруднения из-за того, что MFC является однопоточной моделью (с точки зрения графического интерфейса пользователя), даже несмотря на то, что в течение долгого времени в Windows были реальные упреждающие потоки. [Возможно, я ошибаюсь из-за этого комментария подколенного сухожилия, я давно не использовал Win32].


Обновление:

Исходя из вашего обновления, в котором вы указали, что делаете, я уверен, что вам не разрешено подключаться до создания. Цитата http://msdn.microsoft.com/en-us/library/3d46645f(VS.80).aspx,

Чтобы использовать объект CAsyncSocket, вызовите его конструктор, затем вызовите функцию Create, чтобы создать базовый дескриптор сокета ... и для клиентского сокета вызовите функцию-член Connect.

Что касается того, почему, я думаю, что это дополнительная сложность, потому что Windows должна делать асинхронные сокеты в среде с обработкой событий, так как они не могут блокировать основной поток GUI.

В средах UNIXy либо отсутствует поток событий (обычные процессы), либо сетевые операции просто передаются другому потоку вручную (в приложениях с графическим интерфейсом).

Скорее всего, это было дизайнерское решение, принятое давно в WinSock (возможно, в Windows 3.11, которая была гораздо более строгой средой для выполнения асинхронных операций) и претворенное в жизнь [хотя это и предположение с моей стороны, API сокетов UNIX имеет никогда не было такого асинхронного поведения, когда сообщения перекачивались, всегда использовались select() или потоки / процессы].


Дальнейшее обновление:

Это утверждение (не исключение) обычно возникает, если вы закрыли и / или удалили объект сокета во время ожидающей операции над ним. В вашем случае я бы посоветовал установить соединение, когда вы его закрываете.

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

Это не проблема MFC, это код вашего друга, нарушающий контракт. Если вы выполняете соединение (или любую асинхронную операцию), вам нужно дождаться успеха или неудачи, прежде чем закрывать сокет (или работать дальше) - в этом случае это означает ожидание вызова вашего Функция OnConnect ().

Из памяти вы вызываете create() при создании асинхронного сокета, тогда все остальное происходит в ответ на сообщения, поступающие в очередь сообщений (то есть вызовы ваших OnXXX() функций). Как и все графические элементы Win32, сообщения должны управлять программой (код запускается в ответ на сообщения). Этот код все больше и больше напоминает классическое кодирование, когда программа управляет всем - этот путь - безумие, поскольку ваша программа и асинхронный сокет «поток» борются за контроль.

Я давно не смотрел на это, но вы сможете получить пример программы CHATSRVR, которая покажет вам, как это сделать.

4 голосов
/ 29 апреля 2009

Вероятно, проблема заключается в плохо написанном коде, и есть большая вероятность, что он не имеет ничего общего с MFC. Но из вашего описания трудно сказать наверняка: «У меня есть приложение MFC, которое выдает всевозможные отладочные утверждения. Что мне делать?».

Может быть, что переписать по порядку, но никто не может сказать, не зная больше о приложении. Я думаю, что вы (или ваш друг) должны будете принять это решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...