Коллеги, ситуация такая.
Есть сервер на блокирующих сокетах, один клиент — один поток, т.е. с каждым клиентским потоком связан сокет, который дает ф-я accept.
Поскольку сокеты блокирующие, большую часть времени они сидят на recv.
Когда сервер заканчивает свою работу, он должен закрыть все эти сокеты.
Я пока делаю так — при завершении главного потока пробегаюсь по списку клиентских потоков, ставлю им признак завершения потока и вызываю closesocket для соотв. сокетов. При этом recv завершается с ошибкой WSAECONNRESET. При обработке ошибок я считаю, что если поставлен признак завершения потока и ошибка WSAECONNRESET, то это рассматривается как "сервер вырубают" и ошибкой это не считается.
Вопрос — допустимо ли так делать (т.е. закрывать сокет, принадлежащий одному потоку, из другого). Конечно, можно вызывать не closesocket, а shutdown(fSocket, SD_BOTH), но принцип остается тем же. Может быть, есть способ получше? Как вообще правильно делать по науке?
Здравствуйте, Ahven, Вы писали:
A>Коллеги, ситуация такая. A>Есть сервер на блокирующих сокетах, один клиент — один поток, т.е. с каждым клиентским потоком связан сокет, который дает ф-я accept. A>Поскольку сокеты блокирующие, большую часть времени они сидят на recv. A>Когда сервер заканчивает свою работу, он должен закрыть все эти сокеты. A>Я пока делаю так — при завершении главного потока пробегаюсь по списку клиентских потоков, ставлю им признак завершения потока и вызываю closesocket для соотв. сокетов. При этом recv завершается с ошибкой WSAECONNRESET. При обработке ошибок я считаю, что если поставлен признак завершения потока и ошибка WSAECONNRESET, то это рассматривается как "сервер вырубают" и ошибкой это не считается. A>Вопрос — допустимо ли так делать (т.е. закрывать сокет, принадлежащий одному потоку, из другого). Конечно, можно вызывать не closesocket, а shutdown(fSocket, SD_BOTH), но принцип остается тем же. Может быть, есть способ получше? Как вообще правильно делать по науке?
Вот кусок кода кодорый закрывает сокет...работает корректно...
У меня при попытке закрыть из другого потока на прямую оставался какой то нейзвестный поток
очевидно именно от TSocket не было времени разбирпться......
Во-первых, m_cWorkSocket — какого класса? Стандартный какой-то или Вы сами писали? И, собственно, где закрывается поток, в котором обрабатываются recv и send для данного сокета? Видимо, здесь: m_cWorkSocket->Close()? А как он закрывается?
Во-вторых, там внутри CancelBlockingCall(), часом, нет вызова WSACancelBlockingCall()? А то согласно MSDN последняя функция является устаревшей и нерекомендована к использованию.
Кстати, а как Вы закрывали напрямую из другого потока?
Вы писали 5 мая 2004 г., 17:17:44:
A> Вопрос — допустимо ли так делать (т.е. закрывать сокет, A> принадлежащий одному потоку, из другого). Конечно, можно вызывать A> не closesocket, а shutdown(fSocket, SD_BOTH), но принцип остается A> тем же.
Если вас устраивает такой способ.. Но, ИМХО, у вас кривоватая
архитектура приложения..
A> Может быть, есть способ получше? Как вообще правильно A> делать по науке?
Неблокирующие сокеты использовать не пробовали?
Здравствуйте, Ahven, Вы писали:
A>Здравствуйте, merlinJap, Вы писали:
J>>void CTcpSender::TcpCloseConnections() J>>{ J>> if(m_WorkSocketStatus == sockNewNotCreated) J>> { J>> delete m_cWorkSocket; J>> m_WorkSocketStatus = sockNotNew; J>> } J>> else if(m_WorkSocketStatus == sockCreated) J>> { J>> if (m_cWorkSocket->IsBlocking()) J>> { J>> m_cWorkSocket->CancelBlockingCall(); J>> } J>> m_cWorkSocket->Close(); J>> delete m_cWorkSocket; J>> m_WorkSocketStatus = sockNotNew; J>> } J>>}
A>Спасибо. A>По данному коду есть несколько вопросов:
A>Во-первых, m_cWorkSocket — какого класса? Стандартный какой-то или Вы сами писали? И, собственно, где закрывается поток, в котором обрабатываются recv и send для данного сокета? Видимо, здесь: m_cWorkSocket->Close()? А как он закрывается?
A>Во-вторых, там внутри CancelBlockingCall(), часом, нет вызова WSACancelBlockingCall()? А то согласно MSDN последняя функция является устаревшей и нерекомендована к использованию.
A>Кстати, а как Вы закрывали напрямую из другого потока?
m_cWorkSocket обычный CSocket. Поток создаеться при помощи CreateThread затем блокируеться...на recv при выполнении комманды CancelBlockingCall() снимаеться блокировка и возвращаеться код ошибки... по этому коду..и завершаеться поток обычным выходом...плюс можно еще использовать WaitForSingleObject.
Здравствуйте, Ahven, Вы писали:
A>Здравствуйте, merlinJap, Вы писали:
J>>void CTcpSender::TcpCloseConnections() J>>{ J>> if(m_WorkSocketStatus == sockNewNotCreated) J>> { J>> delete m_cWorkSocket; J>> m_WorkSocketStatus = sockNotNew; J>> } J>> else if(m_WorkSocketStatus == sockCreated) J>> { J>> if (m_cWorkSocket->IsBlocking()) J>> { J>> m_cWorkSocket->CancelBlockingCall(); J>> } J>> m_cWorkSocket->Close(); J>> delete m_cWorkSocket; J>> m_WorkSocketStatus = sockNotNew; J>> } J>>}
A>Спасибо. A>По данному коду есть несколько вопросов:
A>Во-первых, m_cWorkSocket — какого класса? Стандартный какой-то или Вы сами писали? И, собственно, где закрывается поток, в котором обрабатываются recv и send для данного сокета? Видимо, здесь: m_cWorkSocket->Close()? А как он закрывается?
A>Во-вторых, там внутри CancelBlockingCall(), часом, нет вызова WSACancelBlockingCall()? А то согласно MSDN последняя функция является устаревшей и нерекомендована к использованию.
A>Кстати, а как Вы закрывали напрямую из другого потока?
Да забыл CancelBlockingCall() то же стандартная функция...
Здравствуйте, butcher, Вы писали:
B>Если вас устраивает такой способ.. Но, ИМХО, у вас кривоватая B>архитектура приложения..
Почему же кривоватая... Я так понимаю, архитектура вполне очевидная, и, более того, часто используемая. В качестве примера могу привести стандартный класс Delphi TServerSocket. Там идея такая же, если использовать serverType = stThreadBlocking (кстати, и закрывают они эти сокеты тоже из другого потока с помощью closesocket).
Да и в литературе и сети такая модель неоднократно описывается. Например, http://www.citforum.ru/book/cook/winsock.shtml, см. там "Пример реализации TCP эхо-сервера". Да и в прочих статьях я подобное видал, например, здесь где-то на rsdn.ru есть статья про пул соединений (ссылку на память не скажу). Однако, ни в одной статье не нашел ответа именно на этот вопрос — как правильно закрывать клиентский сокет и поток.
А ведь вопрос даже не в моей архитектуре — если использовать блокирующие сокеты, надо иметь возможность как-то их закрывать при завершении работы программы. При этом ясно, что закрывать надо из другого потока — откуда же ещё, если сам поток висит на recv?
B>Неблокирующие сокеты использовать не пробовали?
Пока нет
Но, конечно, с ними я знаком. Однако должен же быть общепринятый способ решать мою задачу. Я думаю (по примеру исходников из Delphi), что общепринятый способ это closesocket или shutdown из другого потока. Вопрос в том, правильно ли я думаю?
Вы писали 5 мая 2004 г., 20:11:02:
A> Почему же кривоватая... Я так понимаю, архитектура вполне A> очевидная, и, более того, часто используемая.
Я понимаю, что она очевидная, но такая архитектура, ИМХО, источник
ошибок в программе, хотя если делать всё грамотно, то я не возражаю
A> Однако, ни в одной статье не нашел ответа именно на этот A> вопрос — как правильно закрывать клиентский сокет и поток.
Ну а какие ещё вы можете придумать способы?
На вскидку, например, использование модели select, через определённый
таймаут проверять готовность к чтению и необходимость к закрытию, но
способ тоже на мой взгляд не очень..
A> А ведь вопрос даже не в моей архитектуре — если использовать A> блокирующие сокеты, надо иметь возможность как-то их закрывать при A> завершении работы программы. При этом ясно, что закрывать надо из A> другого потока — откуда же ещё, если сам поток висит на recv?
ИМХО, закрывать сокет должен тот поток, который его создал. Такой же
точки зрения я придерживаюь и в отношении других дескрипторов,
динамически выделенной памяти и прочего..
B>>Неблокирующие сокеты использовать не пробовали? A> Пока нет A> Но, конечно, с ними я знаком.
Попробуйте, возможно вам понравится
--
С уважением, butcher
Здравствуйте, butcher, Вы писали:
A>> А ведь вопрос даже не в моей архитектуре — если использовать A>> блокирующие сокеты, надо иметь возможность как-то их закрывать при A>> завершении работы программы. При этом ясно, что закрывать надо из A>> другого потока — откуда же ещё, если сам поток висит на recv? B>ИМХО, закрывать сокет должен тот поток, который его создал. Такой же B>точки зрения я придерживаюь и в отношении других дескрипторов, B>динамически выделенной памяти и прочего..
Ну, это бесспорно. Возможно, я не совсем корректно выразился — я имел в виду, что подобным образом сокет закрываю только в случае "жесткой посадки", т.е. когда сервер вырубают невзирая на наличие открытых соединений. В нормальном случае, конечно, клиент сам закрывает соедиение, когда заканчивает диалог — либо сокет закрывает и сервер получает recv() == 0 либо клиент посылает сообщение определенного вида.
B>>>Неблокирующие сокеты использовать не пробовали? A>> Пока нет A>> Но, конечно, с ними я знаком. B>Попробуйте, возможно вам понравится
Наверняка
Ну, в общем спасибо ещё раз. Будем стучать по клавишам