Рализация Multithreaded CSocket - одни вопросы???
От: Finder  
Дата: 30.12.02 10:23
Оценка:
Собственно, сабж. Куча клиентов чего-то спрашивают у сервера, он им чего-то отвечает. Певроначально сервер сделал как консольное приложение с поддержкой одного канала: listen(), accept() — новый сокет, канал отработал, убил, ждем нового соединения. И все работало прекрасно, пока не перешел к следующему шагу: добавить многопоточность (она же многоканальность . После accept() создаю отдельную нитку, в которой и выполняется общение сервера с клиентом. Сервер в это время снова listen(). Однако такой вариант отказывается работать: из недр sockcore.cpp стал вылетать ASSERT(pState->m_hSocketWindow != NULL); Попытался разобраться... и окончательно запутался:
При каждом send()/receive() вызывается одноименный метод CAsyncSocket и если вдруг он возвращает ошибку WSAEWOULDBLOCK CSocket пытается слать мессаги PumpMessage(). Как показала отладка, такая же ситуация бывает и в старой однопоточной версии, но там хэндл окна не пустой (и ассерт не вылазит).
Отсюда два вопроса:
1. CSocket — блокирующий, а в MSDN сказано, что ошибка WSAEWOULDBLOCK возникает, если сокет помечен как неблокирующий и receive() хочет его заблокировать. Не понял, почему эта ошибка вообще возникает...
2. Почему при запуске поддержки соединения в отдельном потоке (AfxBeginThread) хэндл окна оказывается нулевой (откуда он там вообще появляется) и слетает PumpMessage() и как это победить? Кстати, на кой вообще мне нужен этот PumpMessage()?
P.S. Я понимаю, что я многого не понимаю...
Re: Рализация Multithreaded CSocket - одни вопросы???
От: Andrew S Россия http://alchemy-lab.com
Дата: 30.12.02 10:50
Оценка: 22 (2)
Когда я начинал ковырять в MFC, я всегда поражался, для чего сделан такой бесполезный класс, как CSocket. Точнее, почему он сделан именно ТАК. CSocket наследуется от CAsyncSocket, поэтому он не блокирующий в сокетном смысле. Он псевдоблокирующий.
int CSocket::Receive(void* lpBuf, int nBufLen, int nFlags)
{
    if (m_pbBlocking != NULL)
    {
        WSASetLastError(WSAEINPROGRESS);
        return  FALSE;
    }
    int nResult;
    while ((nResult = CAsyncSocket::Receive(lpBuf, nBufLen, nFlags)) == SOCKET_ERROR)
    {
        if (GetLastError() == WSAEWOULDBLOCK)
        {
            if (!PumpMessages(FD_READ))
                return SOCKET_ERROR;
        }
        else
            return SOCKET_ERROR;
    }
    return nResult;
}


За этот код правильно было бы расстрелять. Сразу, без суда, как в военное время. Тут используется скрытое окно, в которое CAsyncSocket спамит сообщениями, да еще и CSocket иногда помогает (а как мы все значем, сообщения в виндовс не самый быстрый способ коммуникации...) Естественно, в твоем треде окна этого нет, вот и не работает ничего (оно одно на все сокеты и создано в другом треде) Более того, если бы окно и было, скорее всего это привело бы к путанице с пакетами, точнее, с очередностью их прихода.
Вывод — товарищи, используйте в сокетах API, а не обертки типа MFC Кстати, еще есть библиотека — raw sockets library или что то в этом роде. Давно было, уже не помню точно. Вот ее использовать было можно, но... все ж api, наверное, проще.
Успехов.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: Рализация Multithreaded CSocket - одни вопросы???
От: Finder  
Дата: 30.12.02 11:07
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>За этот код правильно было бы расстрелять. Сразу, без суда, как в военное время. Тут используется скрытое окно, в которое CAsyncSocket спамит сообщениями, да еще и CSocket иногда помогает (а как мы все значем, сообщения в виндовс не самый быстрый способ коммуникации...) Естественно, в твоем треде окна этого нет, вот и не работает ничего (оно одно на все сокеты и создано в другом треде)


Согласен. Что-ж, наверное будем переходить на API
Копаясь в MSDN (а конретно, в описании метода CSocket::Attach()) наткнулся на пример, где все же используется CSocket в отдельном потоке. Как это работает на самом деле, непонятно, наверное, за счет того, что сокет поддержки соединения содержится в классе, порожденном от CWinThread (Attach()/Detach() там используется для передачи хэндла сокета в поток). На все равно даже этот вариант выглядит кривовато.
Дак что же, народ: нормальный многопоточный сервак не делается на CSocket?
Re[3]: Рализация Multithreaded CSocket - одни вопросы???
От: kmn Украина  
Дата: 30.12.02 11:28
Оценка:
Здравствуйте, Finder, Вы писали:

F>Здравствуйте, Andrew S, Вы писали:


AS>>За этот код правильно было бы расстрелять. Сразу, без суда, как в военное время. Тут используется скрытое окно, в которое CAsyncSocket спамит сообщениями, да еще и CSocket иногда помогает (а как мы все значем, сообщения в виндовс не самый быстрый способ коммуникации...) Естественно, в твоем треде окна этого нет, вот и не работает ничего (оно одно на все сокеты и создано в другом треде):)


F>Согласен. Что-ж, наверное будем переходить на API ;)

F>Копаясь в MSDN (а конретно, в описании метода CSocket::Attach()) наткнулся на пример, где все же используется CSocket в отдельном потоке. Как это работает на самом деле, непонятно, наверное, за счет того, что сокет поддержки соединения содержится в классе, порожденном от CWinThread (Attach()/Detach() там используется для передачи хэндла сокета в поток). На все равно даже этот вариант выглядит кривовато.
F>Дак что же, народ: нормальный многопоточный сервак не делается на CSocket?
F> :crash:

Если стоит задача реализации мноргоканальности при помощи многопоточности, то CSocket для этих целий лучше не использовать.

С другой стороны, мноргоканальность можно реализовать, используя CSocket, в одном потоке.
Re[4]: Рализация Multithreaded CSocket - одни вопросы???
От: Finder  
Дата: 30.12.02 12:06
Оценка:
Здравствуйте, kmn, Вы писали:

kmn>С другой стороны, мноргоканальность можно реализовать, используя CSocket, в одном потоке.


Кроме самого CSocket, сервер еще кое-чего полезного делает, и тут кроме многопоточности альтернативы нет. Кстати, несколько лет назад я уже делал аналогичную вещь на CBuider (исходники только затерялись где-то ) — так вот там таких граблей не наблюдалось...
Re[5]: Рализация Multithreaded CSocket - одни вопросы???
От: Andrew S Россия http://alchemy-lab.com
Дата: 30.12.02 15:29
Оценка:
Не а. В VCL есть подобные же грабли. Они связаны с тем, что треды (TThread) синхронизируются с основным приложением при помощи сообщений. Окно, через которое они это делают, создается при первом создании TThread. А вот когда первый TThread создается не в основном потоке, в котором к тому же нет выборки сообщений... то все становится просто удивительно
А сокетные классы там сделаны похоже, за исключением того, что есть почти нормальный мультитреадный режим (используется асинхронный WriteFile), и в нем работает все почти нормально. Но опять же — сделано почти так же, как в MFC, так что возможно все.
Например, посмотрим Synchronize, интенсивно вызываемый из TServerClientThread
procedure TThread.Synchronize(Method: TThreadMethod);
begin
  FSynchronizeException := nil;
  FMethod := Method;
  SendMessage(ThreadWindow, CM_EXECPROC, 0, Longint(Self));
  if Assigned(FSynchronizeException) then raise FSynchronizeException;
end;


Напоминает MFC, правда?

Так что счастлив Ваш Бог, что проблем не было... Или проблемы просто не были замечены


F>Кроме самого CSocket, сервер еще кое-чего полезного делает, и тут кроме многопоточности альтернативы нет. Кстати, несколько лет назад я уже делал аналогичную вещь на CBuider (исходники только затерялись где-то ) — так вот там таких граблей не наблюдалось...
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[5]: Рализация Multithreaded CSocket - одни вопросы???
От: J.J.OK  
Дата: 30.12.02 15:39
Оценка:
Посмотри внимательно — может проще будет
переехать не на API а на AsyncSocket?
У меня под руками прога — своя SOCKS-прокся (с кучей другого функционала) — так на CAsyncSoket-e сделано — и без проблем
Чем безопаснеe — тем неудобнее ;-)
Re[6]: Рализация Multithreaded CSocket - одни вопросы???
От: Sinclair Россия https://github.com/evilguest/
Дата: 31.12.02 12:36
Оценка:
Здравствуйте, Andrew S, Вы писали:

Небольшое замечание:
Приведенный код — из Delphi 5. В шестерке они сменили механизм. Где-то a форуме Delphi/Builder пробегал постинг ХакераДельфи на эту тему.
Данный код будет правильно работать в том и только том случае, если первый созданный поток является интерфейсным, т.е. производит выборку очереди сообщений, и если он обрабатывает сообщение CM_EXECPROC. В принципе, ребята сделали хорошую работу, т.к. они добились того, что код выполняется реально в главном потоке, а не во вторичном, заблокировав главный. Этакая кооперативная многозадачность. Такая структура позволяет делать VCL потоконебезопасным, и не использовать там никакие механизмы блокировок.
Основной косяк технологии — убийство TThread. Дело в том, что его метод WaitFor весьма нетривиален — если поток попытается вызвать Synchronize до опроверки на Terminated, а главный поток выполняет апишный Wait — тогда все встанет колом. Я напоролся на это, когда пытался сделать убивалку с таймаутом. В итоге пришлось списать с исходников Borland
... << RSDN@Home 1.0 beta 3 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Рализация Multithreaded CSocket - одни вопросы???
От: Sinclair Россия https://github.com/evilguest/
Дата: 31.12.02 12:39
Оценка:
Здравствуйте, Finder, Вы писали:

Насколько я знаю, смысл в том, что в момент Accept создается новый сокет. Он стряпает невидимое окно для получения сообщений о результатах неблокирующих вызовов. Тонкость: в винде окна привязываются к потоку, который их создал. Теперь проблема: окно сокета создано главным потоком, а работает с этим сокетом новый поток. Увы и ах.
Возможный выход: при приходе соединения нужно породить новый поток, который уже сделает Accept и продолжит общение. Не забыть про блокировки между главным потоком и новым в этот момент. Попробуй — может помочь.
... << RSDN@Home 1.0 beta 3 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Рализация Multithreaded CSocket - одни вопросы???
От: Andrew S Россия http://alchemy-lab.com
Дата: 31.12.02 13:33
Оценка:
Нет. Основной косяк — то, что они пытаются создать окно не в _главном_ треде приложения (т.е. в том, в котором живет основная форма), а в том, что окно создается в потоке, в котором происходит первое создание объекта TThread. Очевидно, ежели первый объект TThread создается не в основном потоке, тогда происходит облом... Т.е. тут — явно глюк в коде. Ну а по поводу CSocket я уже сказал Кстати, постинги в форуме Билдер по подобной теме были моими

S>Основной косяк технологии — убийство TThread. Дело в том, что его метод WaitFor весьма нетривиален — если поток попытается вызвать Synchronize до опроверки на Terminated, а главный поток выполняет апишный Wait — тогда все встанет колом. Я напоролся на это, когда пытался сделать убивалку с таймаутом. В итоге пришлось списать с исходников Borland
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[8]: Рализация Multithreaded CSocket - одни вопросы???
От: Sinclair Россия https://github.com/evilguest/
Дата: 05.01.03 12:42
Оценка:
Здравствуйте, Andrew S, Вы писали:

AS>Нет. Основной косяк — то, что они пытаются создать окно не в _главном_ треде приложения (т.е. в том, в котором живет основная форма), а в том, что окно создается в потоке, в котором происходит первое создание объекта TThread. Очевидно, ежели первый объект TThread создается не в основном потоке, тогда происходит облом... Т.е. тут — явно глюк в коде.

А, то есть ты намекаешь, что если мы руками состряпаем тред, в котором попытаемся состряпать TThread, то упс? Похоже на правду. Вообще, конечно, надо уже либо TThread пользовать, либо вручную колбаситься. Ничего хорошего и з сочетания этих двух подходов, имхо, не выйдет.

Ну а по поводу CSocket я уже сказал Кстати, постинги в форуме Билдер по подобной теме были моими
Вообще-то я имел в виду http://www.rsdn.ru/Forum/Message.aspx?mid=69160&amp;only=1
Автор: Mike, однако
Дата: 04.07.02
... << RSDN@Home 1.0 beta 3 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Рализация Multithreaded CSocket - одни вопросы???
От: Andrew S Россия http://alchemy-lab.com
Дата: 05.01.03 14:21
Оценка:
Аа. не, эти не мои. Честно говоря, особого отличия, кроме добавления лишнего кода со списками и событиями я не заметил. В результате то все равно вызывается PostMessage. А можно и SendMessage безо всяких объектов синхронизации — какая собственно разница

S>Вообще-то я имел в виду http://www.rsdn.ru/Forum/Message.aspx?mid=69160&amp;only=1
Автор: Mike, однако
Дата: 04.07.02
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: Рализация Multithreaded CSocket - одни вопросы???
От: LOK Украина  
Дата: 08.01.03 10:17
Оценка: 3 (1)
Здравствуйте, Finder, Вы писали:

Сталкивался с такой проблемой

неоюходимо в потоке перед работой с сокетами вызвать вот такой кусок кода

  #ifndef _AFXDLL
  #define _AFX_SOCK_THREAD_STATE AFX_MODULE_THREAD_STATE
  #define _afxSockThreadState AfxGetModuleThreadState()

   _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
   if (pState->m_pmapSocketHandle == NULL)
       pState->m_pmapSocketHandle = new CMapPtrToPtr;
   if (pState->m_pmapDeadSockets == NULL)
       pState->m_pmapDeadSockets = new CMapPtrToPtr;
   if (pState->m_plistSocketNotifications == NULL)
       pState->m_plistSocketNotifications = new CPtrList;
for {; 1.0 beta 4 ;Hi-Fi — Средняя школа }
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.