Форум: "Сети";
Текущий архив: 2004.06.13;
Скачать: [xml.tar.bz2];
ВнизWinsock2 Найти похожие ветки
← →
Тимохов © (2004-04-23 11:02) [0]Добрый день, уважаемые мастера.
Если позволите хотел бы получить оценку следующей реализации сетевого взаимодействия.
Есть связка - клиент+сервер. Оба мои и на дельфи.
Интерфейс взаимодейтствия в упрощенном виде выглидит так:
Id := Login()
Blob := Get(Id)
Set(Id, Blob)
{Команды get и set могут вызываться в любом порядке и в любом количестве.}
Logout(Id)
Взаимодействие между клиентом и сервером осуществлятся через непосредственные вызовы функций из winsock2 (т.е. компонетами дельфи пока не пользуемся, почему - отдельных разговор).
Сервер в доп. потоке (назовем его П1) пытается через accept получить соединение. Как только accept возвращает ид. сокета для обмена данными запускается новый поток (назовем его П2), который сначала получает вопрос от клиента, затем отправляет ответ. После чего сокет для обмена данными закрывается и поток П2 останавливается. Поток П1 продолжает получать запросы от клиентов. Следующий вопрос+ответ с тем же клиентом будет обработан в новом потоке и новом сокете, после чего и поток и сокет удаляться и т.д.
Сокеты используются синхронные и вроде как блокирующие (не совсем пока понял чем это отличается).
Хотелось бы услышать ответ на вопрос: делает ли так вообще кто-то или нужно держать соединене для каждого клиента начиная с Login и заканчивая Logout?
Заранее спасибо.
← →
Digitman © (2004-04-23 11:06) [1]
> поток П2 останавливается
останавливается или терминируется ? разные же вещи ..
вообще-то в "Потрепаться" я тебе уже ответил на этот вопрос
← →
Тимохов © (2004-04-23 11:07) [2]Так получилось, что этот же вопрос я задавл тут
http://delphimaster.net/view/15-1082641309/.
В продолжение ответа, который дал Digitman по указанной ссылке хотелось бы задать уточняющий вопрос.
Насколько я понял ничего ужасного в открытии на каждый вопрос+ответ нового сокета и созадние потока ничего ужасного нет, кроме того, что это трата ресурсов?
В моем случае нагрузка будет очень низкая - порядка 50 кб раз в 5 мин. Думаю что ресурсы можно не экономить?
← →
Тимохов © (2004-04-23 11:07) [3]
> Digitman © (23.04.04 11:06) [1]
Согласен - разные.
Я имел в виду, что заканчивается execute, т.е. управление доходит до конца.
← →
Григорьев Антон (2004-04-23 11:10) [4]Это что, получается, что клиент для каждого запроса должен делать новый connect? ИМХО, непроизводительно, хотя работать будет. Ведь протокол TCP для того и предназначен, чтобы один раз установить соединение и держать его до конца. Если вам по каким-то причинам постоянное соединение не нужно, лучше присмотритесь к протоколу UDP.
P.S. Кстати, при чём тут WinSock 2? То, что вы описали, укладывается не только в WinSock 1, но и даже в Berkley Sockets. WinSock начинается как раз там, где используются асинхронные сокеты, которых у вас нет.
← →
Тимохов © (2004-04-23 11:15) [5]
> Григорьев Антон (23.04.04 11:10) [4]
У меня задача маленькая - убрать старую версию сетевого взаимодействия - через файлы на общем дисковом ресурсе. Такой механизм просуществовал лет 8, наверное, сейчас однозначно требует переделки.
Поэтому если работать будет, пусть и непроизовдительно, то и это хорошо.
> WinSock начинается как раз там, где используются асинхронные
> сокеты, которых у вас нет.
Согласен - вчера ночью читал.
Да надо же както было тему называть :)))
← →
Rouse_ © (2004-04-23 11:16) [6]В принципе нормальный алгоритм работы, но при такой постановке задачи, согласен с Антоном - лучше использовать UDP.
← →
Rouse_ © (2004-04-23 11:18) [7]> У меня задача маленькая - убрать старую версию сетевого
> взаимодействия - через файлы на общем дисковом ресурсе
Кстати - еще проще: чтобы не влазить в глубину WinSock API как вариант можно использовать Mailslots. Это наименее трудоемкая задача по реализации, а эффект будет аналогичный...
← →
Тимохов © (2004-04-23 11:21) [8]
> Rouse_ © (23.04.04 11:18) [7]
Говорил мне кто-то про них.
Видел уже в MSDN, но пока не дошел.
Спасибо, почитаю.
← →
Digitman © (2004-04-23 11:25) [9]
> ничего ужасного нет ..?
нет , конечно
"ужасы" начинаются тогда, когда клиент выполнив коннект и тут же послав запрос ждет от сервера как можно более быстрый ответ, а сервер тем временем еще только стартует новый транспортный поток на своей стороне для обслуживания подключившегося клиента .. вместо того чтобы взять уже ранее стартованный поток из пула свободных
> Григорьев Антон (23.04.04 11:10) [4]
> Это что, получается, что клиент для каждого запроса должен
> делать новый connect?
в принципе-то ничего страшного .. ну а как тот же IE работает при клике на гипертекстовую ссылку ? те же "коннект" + Post/Get + "дисконнект" .. в случае с http это вполне обоснованная логика
← →
panov © (2004-04-23 11:29) [10]>Григорьев Антон (23.04.04 11:10) [4]
Это что, получается, что клиент для каждого запроса должен делать новый connect?
Нет, новый коннект не делается.
Описанный в топике алгоритм работы - это стандартный подход при реализации сервера.
>Тимохов © (23.04.04 11:15) [5]
Поэтому если работать будет, пусть и непроизовдительно, то и это хорошо.
Да замечательно будет работать... Не вижу причин для потери производительности..
← →
Тимохов © (2004-04-23 11:34) [11]
> Digitman © (23.04.04 11:25) [9]
Суммирую Ваши рассуждения и прихожу к выводу, что лучше всего сделать так (приведу на примере последовательности действий):
1. В П1 accept возвращает датаобменный сокет С1.
2. Стартуется поток П2, который делает recv и send с сокетом С1.
3. Сокет С1 закрывается (closesocket)
4. Поток П2 приостанавливается и записывается в кладоску потоков.
5. В П1 accept возвращает еще один датаобменный сокет С2.
6. Просматривается кладовка потоков, если есть свободный, то ему передается С2 и делается resume.
7. и т.д.
Так лучше?
← →
Григорьев Антон (2004-04-23 11:35) [12]
> panov © (23.04.04 11:29) [10]
> >Григорьев Антон (23.04.04 11:10) [4]
>
> Это что, получается, что клиент для каждого запроса должен
> делать новый connect?
>
> Нет, новый коннект не делается.
> Описанный в топике алгоритм работы - это стандартный подход
> при реализации сервера.
В вопросе написано: "Следующий вопрос+ответ с тем же клиентом будет обработан в новом потоке и новом сокете..." Откуда возьмётся новый сокет без connect"а?
← →
Григорьев Антон (2004-04-23 11:41) [13]
> Тимохов © (23.04.04 11:34) [11]
> [Skipped]
> > Так лучше?
Нет. Лучше так:
1. В П1 accept возвращает датаобменный сокет С1.
2. Стартуется поток П2, который делает recv и send с сокетом С1.
3. Для дальнейших запросов клиент не делает нового connect"а, а пользуется тем, который был сделан раньше. Соответственно, серверной стороне нет смысла закрывать сокет и завершать нить - эта нить реализует цикл из recv/send, который оканчивается только в том случае, если приходит запрос на logout или происходит аварийный разрыв соединения. При этом завершается поток.
4. В П1 accept возвращает еще один датаобменный сокет С2 для другого клиента, для которого создаётся аналогичный поток.
5. и т.д.
Не возитесь с кладовкой потоков, просто создавайте каждый раз новый и удаляйте их, как только сеанс обмена с клиентом завершён - так будет легче. И ограничьтесь одним соединением от login до logout - это тоже проще.
← →
Digitman © (2004-04-23 11:44) [14]
> Тимохов © (23.04.04 11:34) [11]
да... именно так и организован трэд-кэш в TServerSocket в режиме stThreadBlocking .. хотя от кэширования всегда можно и отказаться, установив размер кэша = 0
← →
Тимохов © (2004-04-23 11:48) [15]
> Григорьев Антон (23.04.04 11:41) [13]
Сколько людей, столько и мнений.
Вот Digitman, например, говорит, что наоборот (я так понял) - потоки в кладовке держать надо, а вот соединения можно какждый раз новые делать.
Еще вопрос на вот эту фразу "эта нить реализует цикл из recv/send". В моем случае сокеты синхронные (правда digitman говорит, что я пользуюсь неправильной терминологией, но все же), т.е. ждут (висят) на recv пока клиент, что нить не пошлет. В данной конфигурации я не понимаю как "нежно" оканчивать поток реализующий цикл recv/send - termiante ничего не даст, т.к. в этом потоке нет цикла с проверкой terminated... :((
← →
Григорьев Антон (2004-04-23 11:55) [16]
> Тимохов © (23.04.04 11:48) [15]
>
> Еще вопрос на вот эту фразу "эта нить реализует цикл из
> recv/send". В моем случае сокеты синхронные (правда digitman
> говорит, что я пользуюсь неправильной терминологией, но
> все же), т.е. ждут (висят) на recv пока клиент, что нить
> не пошлет. В данной конфигурации я не понимаю как "нежно"
> оканчивать поток реализующий цикл recv/send - termiante
> ничего не даст, т.к. в этом потоке нет цикла с проверкой
> terminated... :((
Проверка Terminated нужна тогда, когда нить должна завершиться по команде извне. А так - просто завершайте работу функции Execute, поток сам и завершится. Примерно такprocedure Execute;
begin
while True do
begin
recv(Socket, ....);
send(Socket, ....);
if <получен logout> then
Break
end;
ShutDown(Socket,SD_Both);
CloseSocket(Socket)
end;
← →
Тимохов © (2004-04-23 12:07) [17]
> Проверка Terminated нужна тогда, когда нить должна завершиться
> по команде извне. А так - просто завершайте работу функции
> Execute, поток сам и завершится.
Вы немного не поняли мой вопрос.
Как внешним образом мягко (т.е. не через termiantethread) остановить поток, который имеют приведенную вами потоковую функцию?
Повторю - recv виснет пока ни чего не придет.
← →
Digitman © (2004-04-23 12:08) [18]
> Тимохов © (23.04.04 11:48) [15]
> ждут (висят) на recv пока клиент, что нить не пошлет
да, ждут .. но не "что нить пошлет", а не менее чем столько, сколько указанго параметром в recv
> не понимаю как "нежно" оканчивать поток реализующий цикл
> recv/send - termiante ничего не даст, т.к. в этом потоке
> нет цикла с проверкой terminated
резонный вопрос..
здесь есть два выхода
1) перейти на неблок.режим работы гнезд с асинхронными нотификациями о транспортных событиях
2) оставить блок.режим, но в потоке П1 при закрытии сервера не командовать потоку П2 на завершение, а просто закрыть гнездо, с которым работает П2 .. после закрытия гнезда "извне" recv() немедленно вернет управление, после чего П2 должен проанализировать код возврата ф-ции - он укажет на соотв.ошибку, по факту которой П2 либо немедленно завершает свое выполнение либо уходит в "кладовку"
← →
Тимохов © (2004-04-23 12:16) [19]
> да, ждут .. но не "что нить пошлет", а не менее чем столько,
> сколько указанго параметром в recv
факт интересный. Не могли бы вы пояснить как он соотносится со следующий фразой из msdn
"For connection-oriented sockets (type SOCK_STREAM for example), calling recv will return as much information as is currently available-up to the size of the buffer supplied."
это текст к recv.
← →
Polevi © (2004-04-23 12:22) [20]>Тимохов © (23.04.04 12:16) [19]
никак, будет ждать пока сколько-нибудь не появится байт в буфере
Digitman ошибся
← →
Тимохов © (2004-04-23 12:25) [21]
> Polevi © (23.04.04 12:22) [20]
Я понимаю, что нельзя просто написать recv(..., 100{байт}, ...). Надо еще проверить результат, что считано 100 байт. И если считано меньше, то попытаться считать остаток и т.д. покак не будет считано все 100 байт.
Правильно?
← →
Digitman © (2004-04-23 12:30) [22]
> Тимохов © (23.04.04 12:16) [19]
обмишулился я здесь ... хотел сказать "не более", а написал "не менее"
← →
Digitman © (2004-04-23 12:31) [23]
> Тимохов © (23.04.04 12:25) [21]
да, правильно
← →
Тимохов © (2004-04-23 12:37) [24]Спасибо все за ответы!
Самое главное, что я понял:
1. Догм и обязательных для вполенения паттернов здесь нет.
2. Моя идея с кучей сокетов и потоков работать будет, но лучше сделать кучу сокетов и мало повторноиспользуемых потоков.
3. Можно сделать также и постоянно живущие коннеты и постоянно живущие потоки.
Одним словом - делать можно по-разному, работать все равно будет.
← →
Digitman © (2004-04-23 12:49) [25]
> Тимохов
да, работать будет хоть так хоть иначе
но - повторюсь еще раз - не следует рассматривать сколь-либо серьезно тот или иной вариант БЕЗ привязки к требованиям конкретного прикладного протокола инф.обмена, т.е. точная реализация протокола - это цель, выбор той или иной транспортной схемы для корректного обмена по этому протоколу - это средство достижения цели
← →
Тимохов © (2004-04-23 12:55) [26]Да в общем-то в начале вопроса я и привел приктически в точности протокол обмена информацией.
Сконектился (login)
Послал вопрос (post)
Получил бинарные данные (get)
Послал вопрос
и т.д.
Отсоединился. (logout)
Все!
Сервер по своей инициативе не может обращаться к клиенту.
Только клиент к серверу.
Протокол - проще некуда.
Еще есть одно требование, клиент - периодический (например, раз в 5 секунд) справшивает сервер о своем состоянии - ответ 1 байт.
Обмен именно данными происходит для каждого клиента раз в 5-10 минут - ответ 15кб.
Клиентов может быть до 30, в среднем 8-10.
← →
Digitman © (2004-04-23 13:01) [27]
> раз в 5 секунд
а вот это уже существенное замечание !
не думаю, что схема "коннект + опрос-состояния + дисконнект" при периодичности в 5 сек. оправдана
в этом случае как раз более оправдана схема с единожды устанавливаемым соединением
← →
Тимохов © (2004-04-23 13:08) [28]Виноват, в начале вопроса забыл сказать про эти 5 секунд.
Понимаю, что это важно.
> в этом случае как раз более оправдана схема с единожды устанавливаемым
> соединением
Я так понимаю, что доп. поток должен постоянно сидеть на recv и жадать сообщений от клиента?
Все-таки к вопросу как прерывать поток, который сидит на recv?
Я так понял, что Вы советуете просто закрывать сокет, которого ждет поток, т.е. closesocket. И recv сразу перестанет ждать и отвалится. Мое же дело просто проверить код возврата и корректно окончить execute потока. Так?
← →
Digitman © (2004-04-23 13:23) [29]
> Я так понимаю, что доп. поток должен постоянно сидеть на
> recv и жадать сообщений от клиента?
в случае блок.режима у транспортного потока выбор в этом плане мал - либо "висеть" на recv либо на select (в посл.случае, правда, блокирующее ожидание можно прерывать по тайм-ауту)
> к вопросу как прерывать поток
да, так
но я все же в который раз рекомендую в тр.потоке работать с гнездом в неблок.режиме
это очень удобно, кр.того потенциальные будущие изменения протокола инф.обмена (ПИО) весьма легко адаптируются в алгоритме тр.потока
в этом случае метод Execute даже не подлежит переделке - он изначально содержит "универсальный" цикл
(псевдокод)
while not Terminated and Socket.Connected do
case MsgWaitForMultipleObjects(SocketEvent, ..) of
wait_object_0: //произошло тр.событие - идем на его анализ и обработку
wait_object_0 + 1: while PeekMessage(..) do Dispatch(); // потоку посланы сообщения - обработаем их
end;
end;
Страницы: 1 вся ветка
Форум: "Сети";
Текущий архив: 2004.06.13;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.036 c