Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2007.02.25;
Скачать: [xml.tar.bz2];

Вниз

Логика работы сокетов   Найти похожие ветки 

 
_Sergey_ ©   (2006-09-05 22:57) [0]

Здравствуйте.
Недавно занялся сетевым программированием с использованием библиотеки WinSock (без применения каких-либо компонентов). Основная задача: написать сервер, который допускал бы подключение только одного клиента. Использую блокирующие сокеты, 1 поток и ф-ию select перед recv для осуществления отключения клиента от сервера по тайм-ауту передачи, клиент и сервер - консольные приложения. При этом возникли некоторые, как я полагаю, логические противоречия в работе сокетов.
1. перевожу сервер в режим ожидания подключения ф-ией listen, жду подключения от клиента, после подключения вызываю ф-ию accept. На этом этапе уже существуют 3 сокета:
1) клиентский
2) связанный с ним серверный
3) серверный в режиме ожидания подключения
Вызываю accept для 1-го клиента. После этого получаю данные от 1-го клиента. Но при этом подключаюсь к серверу 2-ым клиентом. Ф-ия connect 2-го клиента завершается без ошибки (2-й клиент добавляется в очередь на подключение). Самым "обидным" является то, что 2-ой клиент продолжает передавать данные, при этом ошибки send у него не появляется, т.е., он передает данные, которые уходят "в никуда". Вопрос: почему send отсылает данные без возникновения ошибок?
2. В обход сложившейся ситуации поступаю следующим образом: после подключения 1-го клиента вызываю CloseSocket для слушающего сокета сервера. При этом 1-й клиент передает данные без проблем (так и должно быть). Но возникает другая проблема: у 2-го клиента удачно завершается ф-ия connect (к какому слушающему сокету он подключается?). Но при первой же попытке передачи данных серверу 2-м клиентом возникает ошибка send. После этой ошибки клиент уничтожает свой сокет и переподключается. Сервер же после отключения 1-го клиента создает заново слушающий сокет и ожидает подключения. Вопрос: почему ф-ия connect 2-го клиента не завершается по ошибке (слушающий сокет на стороне сервера разрушен же)?

Изучал RFC793, RFC791, RFC768. Ситуация не прояснилась. Есть предположение, что для UDP и TCP-сокетов создаются одни и те же программные структуры (сокеты). Кроме того, есть предположение, возникшее после прочтения спецификаций, что слушающий сокет отличается от обычного только установкой спецфлага, т.е., имеет вх. и вых. буферы, и обмен информацией даже со слушающим сокетом не запрещен. И так, как в UDP реализуется передача данных до установления соединения, то реализуется ситуация 1 (данные записываются во вх. буфер слушающего сокета, ошибка не возникает). 2-я ситуация: ф-ия connect проверяет только наличие сокета, но не проверяет, слушающий он или нет. Поправьте меня, если я ошибаюсь.
Я задавал подобный вопрос на одном из форумов: http://www.delphikingdom.com/asp/answer.asp?IDAnswer=44294
Ответа на свой вопрос я, к сожалению, не получил. Буду рад любой информации: ссылки на документацию, адреса специализированных форумов, на кот. я могу получить ответы на мои вопросы.


 
Сергей М. ©   (2006-09-06 08:26) [1]


> задача: написать сервер, который допускал бы подключение
> только одного клиента


Для Win9x/Me/NT задача не решаема.
Сабж реализуем только для линейки ОС, начиная с W2k, где в отличии от предшествующих ОС функционирует опция  гнезда SO_CONDITIONAL_ACCEPT (см. Get/SetSockOpt). Только включив эту опцию и используя WSAAccept() вместо accept() можно решить задачу так как она поставлена.


 
Сергей М. ©   (2006-09-06 08:40) [2]

Впрочем можно "извратиться" и следующим образом:

При вызове listen() указать 2-м параметром (backlog queue size) значение = 1.
После успешного возврата из accept() тут же закрыть слушающее гнездо и создавать его вновь лишь при разрыве связи с клиентом.

100%-й гарантии того, что ни один клиент более не подключится, нет и в этом случае, но на практике этого достаточно для реализации сабжа.


 
_Sergey_ ©   (2006-09-06 15:27) [3]

Спасибо за ответ. Задача стоит таким образом, чтобы мое приложение (и сервер, и клиенты) работали, по крайней мере, начиная с Win98. Путем многих проб и экспериментов я реализовал логику, которая основывалась на втором посте. При проведении экспериментов с сокетами (W2k SP4)обнаружил, что параметр backlog queue size не оказывает никакого влияния на указанную ситуацию. При подключенном в данный момент 1-м клиенте 2-й подключался и при значении указанного выше параметра 1, 0, в данный момент этот параметр имеет значение SoMaxConn.
1-ый указанный Вами вариант реализовавал. Действительно, сервер не допускал подключения других клиентов, если хотя бы один был подключен (CF_REJECT). Но при оч. интенсивной нагрузке на сервер и большом кол-ве клиентов многие клиенты "зависали" (т.е., как я понял, не получали возврата из ф-ий библиотеки сокетов). Поэтому был реализован 2-й вариант. И как программист, который хочет понимать все в своей программе, возникли вопросы относительно логики работы сокетов. Соответствено, 1-ый и 2-ой вопросы в моем первом посте. Ведь протокол TCP/IP предотвращает дублирование и потерю пакетов, восстанавливает первоначальную очередность следования пакетов, предотвращает повреждение информации при передаче по линиям связи. Но, получается, то, что реализуется на нижнем уровне, то, о чем позаботились разработчики на нижнем уровне реализации, с тем приходится бороться на прикладном уровне, потому как мое приложение после введения обратной связи застраховано от потери пакета данных (пакет данных обязательно дойдет к серверу), но не застраховано от дублирования пакетов (маловероятная ситуация, но возможная).


 
Сергей М. ©   (2006-09-06 15:40) [4]


> _Sergey_ ©   (06.09.06 15:27) [3]


> при оч. интенсивной нагрузке на сервер и большом кол-ве
> клиентов многие клиенты "зависали"


Ты о чем ? О "зависании" тех самых "многих" на вызове ими ф-ции connect() ?


 
Сергей М. ©   (2006-09-06 15:46) [5]


> не застраховано от дублирования пакетов


Что за ерунда ?
Как это связано с транспортным уровнем ?


 
Сергей М. ©   (2006-09-06 16:10) [6]


> 2-ой клиент продолжает передавать данные, при этом ошибки
> send у него не появляется, т.е., он передает данные, которые
> уходят "в никуда". Вопрос: почему send отсылает данные без
> возникновения ошибок?


Потому что он в этот момент уже успешно "законнекчен".


> в данный момент этот параметр имеет значение SoMaxConn


А должен при любой логике быть = 1.

p.s.

Ты циклограмму установления TCP-соединения изучал ?


 
Медведъъ   (2006-09-06 16:55) [7]

while true do
begin
 listen
 accept
 recv
 send
 shutdown
 closesocket
end

больше одного ну никак не подключится, если поток один


 
Медведъъ   (2006-09-06 16:57) [8]

аа ступил, сорри


 
Сергей М. ©   (2006-09-07 08:41) [9]


> Медведъъ   (06.09.06 16:57) [8]
> ступил


Да уж) .. Ерунду сморозил)


 
_Sergey_ ©   (2006-09-07 17:59) [10]


> Ты о чем ? О "зависании" тех самых "многих" на вызове ими
> ф-ции connect() ?

Точно сказать не могу, на вызове какой ф-ии некоторые клиенты оказывались в "зависшем" состоянии, но логика работы клиента такова:
создать сокет, подключиться, послать нулевой пакет для проверки связи, послать информацию и через select ожидать (принять) подтверждение доставки от сервера (обратная связь). Если возникнет какая-нибудь ошибка, то уничтожить сокет и повторить все со слов "создать сокет". При этом все возникающие ошибки и некот. информация об успешно отправленном пакете записываются в логи. При небольш. кол-ве клиентов никаких проблем не возникает. Но при большом кол-ве клиентов и 1-м сервере, запущенных на локальной машине, возникает ситуация, когда через некот. время клиенты и не пишут в логи, и не обновляют статинфо на консоли, и не производят никаких других указанных действий.


 
_Sergey_ ©   (2006-09-07 18:15) [11]


> Что за ерунда ?
> Как это связано с транспортным уровнем ?

Связано, как я писал, с ПРИКЛАДНЫМ уровнем. Связано, поскольку при большой нагрузке на сервер возникает ситуация, когда, несмотря на все проверки, сервер все же не получает информацию, а клиент считает, что он ее успешно отправил. Ранее я писал клиент, основывающийся на ф-ии TransmitFile. Впоследствии мне пришлось отказаться от такой реализации.Статистика:
1) 15 клиентов на основе TransmitFile, 1 сервер, запущены локально, тайм-аут у клиента при неудачной передаче - 30 мс. На сервере фиксировалось около 20 000 успешных передач от клиента. При этом у самих клиентов суммарное кол-во успешных отправлений было где-то на 20-30 больше. Вывод: на 20 тыс отправок в жестком тестировании терялось около 20-30 пакетов.
2) 15 клиентов на основе send, те же условия.
На 20 тыс. потери около 2000 - 2500. Разница налицо.

Как в 1-м, так и во 2-м случае потери есть. А нужно, чтоб их не было. Решение: ввел обратную связь. Теперь после каждого полученного "правильного" пакета сервер отсылает клиенту сообщение, кот. клиент принимает, разрывает связь (уничтожает сокет) и формирует след. пакет.
Если подтверждение не пришло, то пакет заново перепередается.

Вывод: пришлось создавать свой простейший протокол.
Результат: кол-во успешно отправленных и успешно принятых пакетов совпадает 1:1.
Вывод: если бы не было указанных странностей в работе сокетов, то не пришлось бы вводить обратную связь. Ошибка фиксировалась бы в момент попытки выполнить сет. ф-ию.


 
_Sergey_ ©   (2006-09-07 18:21) [12]


> Потому что он в этот момент уже успешно "законнекчен".

Странно. Насколько я читал, только при вызове accept создается сокет, связанный с текущим. Для 2-го клиента я accept не вызывал. Значит, получается, и сокета для него на стороне сервера нет. Если нет связанного сокета на стороне сервера, значит, и "законнекченным" он быть не может. А данные куда-то уходят. Причем, без фиксации ошибок.


 
_Sergey_ ©   (2006-09-07 18:32) [13]


> Ты циклограмму установления TCP-соединения изучал ?

Нет. Но нашел. http://contacts.narod.ru/TCPIP/tcp18.html#t182000
Пока я не понял, как это связано с обсуждаемыми вопросами. Обмен SYN, ACK, FIN - это внутренняя логика. Она жесткая, ее изменить нельзя, пользуясь библиотекой сокетов.


 
Сергей М. ©   (2006-09-08 11:30) [14]


> Если подтверждение не пришло, то пакет заново перепередается


Это нормально для прикл.протоколов на базе UDP, но совершенно не нормально для TCP-базы.

Если подтверждение не поступило в заданный период прикладного таймайта ожидания, и при этом канал связи якобы активен, значит либо противоположная сторона соединения чем-то занята (что ненормально с т.з. оговоренной прикладной логики) либо произошел разрыв канала связи по причинам, не зависящим от партнера по соединению.
Для отслеживания таких ситуаций как правило задействуется т.н. "KeepAlive"-механизм. Суть его проста - периодически "прощупывать" виртуальное соединение на предмет отказа, посылая партнеру по соединению спец.инф.пакеты а-ля "Я живой". Если в момент отправки пакета произошла искл.ситуация на уровне Winsock-функции send(), ее следует интерпретировать как разрыв канала связи по причинам, не зависящим от партнера, не ждать более никаких ответов от партнера (ибо канала связи уже не существует) и закрывать гнездо со своей стороны.
В нек.случаях можно задействовать "KeepAlive"-механизм на внутреннем уровне WSP (см. п.п. 4.2.3.6 RFC 1122 и ф-цию SetSockOpt(SO_KEEPALIVE)), в иных же случаях механизм реализуется на прикладном уровне.


> _Sergey_ ©   (07.09.06 18:21) [12]


Изучи внимательно циклограмму стандартной процедуры установления ТСР-соединения

http://book.itep.ru/4/44/tcp_443.htm

Как видишь, accept() нигде не фигурирует - за прослушивание, прием и подтверждение вх.запросов на соединение отвечает listen-механизм, accept лишь выбирает из очереди (backlog queue) уже подтвержденных запросов (если таковые имеются) очередной запрос и создает для него отдельное гнездо, завершая тем самым создание виртуального соединения.
Т.е. акцептирование сервером соединения в этом случае происходит уже на уровне listen-механизма.

В случае же условного акцепта (SO_CONDITIONAL_ACCEPT) listen-механизм ведет себя иначе - в ответ на запрашивающий клиентский SYN-пакет он задерживает отправку  подтверждающего SYN-ACK-пакета до момента вызова WSAAccept(), которая может как подтвердить, так и отложить или отвергнуть запрос на соединение. В случае подтверждения SYN-ACK-пакет немедленно отправляется клиенту, в случае отложения запроса он ставится, если не ошибаюсь, в хвост backlog-очереди, в случае отвержения клиенту отправляется RST-пакет.


> данные куда-то уходят. Причем, без фиксации ошибок


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


 
Сергей М. ©   (2006-09-08 11:31) [15]


> Точно сказать не могу, на вызове какой ф-ии некоторые клиенты
> оказывались в "зависшем" состоянии


Для этого существует отладчик.


 
_Sergey_ ©   (2006-09-09 00:16) [16]


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

Keep-Alive задействовать в моем случае нерационально, т.к. мой сервер предназначен для получения коротких сообщений, которые могут поступать с разным интервалом времени от многих клиентов. Клиенты при занятости сервера ожидают окончания обслуживания ним текущего подключенного клиента. Клиенты - приложения, работающие в автоматическом режиме, не ожидающие ввода команд пользователя при работе с сервером. После окончания формирования информации, подлежащей отправке на сервер, клиент пытается ее отправить. При этом пакет небольшой, времени такая пересылка занимает оч. мало, после чего (в случае успешной передачи) клиент разрывает соединение.

> в иных же случаях механизм реализуется на прикладном уровне.

Да. У меня это реализуется так: посылка перед передачей основного пакета с информацией пакета с нулевым размером. При ошибке - уничтожить сокет, создать его заново и повторить процесс передачи заново.


> Если в момент отправки пакета произошла искл.ситуация на уровне
> Winsock-функции send(), ее следует интерпретировать как разрыв канала
> связи по причинам, не зависящим от партнера, не ждать более никаких
> ответов от партнера (ибо канала связи уже не существует) и закрывать
> гнездо со своей стороны.

Я так и делаю. От сервера не пришло подтверждение - уничтожаю сокет и повторяю процедуру подключения.


> Это нормально для прикл.протоколов на базе UDP, но совершенно не
> нормально для TCP-базы.

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


Для этого существует отладчик.

SoftIce-ом пользоваться так и не научился. Раньше отлаживал разные ассемблерные программки под DOS досовскими отладчиками. Так что опыт есть. Хоть и не подхлдящий для этой задачи, но принципы я прочувствовал еще тогда. Могу сказать следующее: только при интенсивном тестировании возникают такие "глюки". При нагрузке 15 клиентов + 1 сервер, 20-30 мс - тайм-аут на реконнект у клиентов, при этом через 15-20 мин. у 4-5 клиентов наступала такая ситуация. По моим представлениям такие "глюки" выловить отладчиком практически невозможно.

P.S> Спасибо за ответ. Все начинает проясняться. Но остался еще вопрос, заданный в самом начале обсуждения:
1-й клиент подключен, уничтожаю слушающий сокет, почему ф-ия connect 2-го клиента при этом не завершается по ошибке (слушающий сокет на стороне сервера разрушен же)?


 
Eraser ©   (2006-09-09 00:37) [17]

> [16] _Sergey_ ©   (09.09.06 00:16)

при чем тут софтайс??? в Делфи есть приличный встроенный отладчик..


 
_Sergey_ ©   (2006-09-09 01:46) [18]


> при чем тут софтайс??? в Делфи есть приличный встроенный
> отладчик..

При том, что 15 клиентов и 1 сервер с тайм-аутами 20-30 мс на переподключение ни на каком отладчике не отладишь. Тем более на встроенном, т.к. не знаешь, для какого из этих 15 клиентов не вернется управление из ф-ии библиотеки сокетов. А только при таком тестировании обнаруживается описываемая ситуация. Т.н. стресс-тест.


 
Eraser ©   (2006-09-09 01:57) [19]

> [18] _Sergey_ ©   (09.09.06 01:46)


> При том, что 15 клиентов и 1 сервер с тайм-аутами 20-30
> мс на переподключение

для современных локальных сетей и тем более Интернета, какие-то нереальные цифры вы хотите выжать...
imho нужно менять логику работы программы, особенно убрать вот это

> написать сервер, который допускал бы подключение только
> одного клиента


 
_Sergey_ ©   (2006-09-09 14:41) [20]


> imho нужно менять логику работы программы, особенно убрать
> вот это

Я считаю, что это как раз не нужно менять. Причины:
1) У сервера есть некот. полезная нагрузка по обработке приходящих пакетов. Нужно, чтобы в каждый момент времени только 1 пакет обрабатывался. Если я создам сервер, рассчитанный на подключение многих клиентов, то придется каким-то образом переводить подключенных клиентов в состояние ожидания, а затем высылать с сервера им спецпакеты на разрешение передачи. А это уже нехорошо, т.к. при длительной работе и некот. сбоях возможны незакрытые сокеты, зависшие соединения, и т.п. (теоретически). Сейчас же я контролирую только 1 соединение, и слежу только за его работой. И все. Больше 2 открытых сокетов мое приложение не создает. Другой способ выйти из этой ситуации: если сервер занят обработкой пришедшего пакета, можно не ожидать окончания его обработки, а накапливать все пришедшие пакеты в каком-то буфере или файле. А затем, при освобождении сервера, постепенно обрабатывать этот файл. Но это тоже не годится. Сервер должен успевать обрабатывать вх. информацию, а не просто сбрасывать ее и откладывать ее обработку на потом. Иначе теряется логика работы моего приложения.
2) Для того, чтобы отслеживать ситуацию, когда еще не создан связанный с клиентским серверный сокет, придется вводить передачу дополнительных служебных пакетов. В текущей реализации этого нет.
3) Время приема пакетов сервером некритично. Если сервер не может сейчас принять пакет, то клиент переконнекчивается через тайм-аут секунд. При этом можно быть всегда уверенным, что, если сервер не может сейчас обслужить клиента, значит, он сейчас принимает или обрабатывает данные от очередного клиента, т.е., занят полезным "делом". И в этой реализации сервер всегда будет успевать обрабатывать данные от клиентов. Если клиентов будет оч. много, то все они будут пытаться отослать информацию по тайм-ауту. Это создаст дополнительную нагрузку на сеть, но в условиях хорошего канала связи или локальной сети это несущественно.
4) Пытался написать попроще. Всего 1 клиент. Думал, по логике вещей, это должно быть реализовано. Ведь, если не отлажена нормально связь с 1 клиентом, то как можно делать многоклиентский сервер - все каналы связи, кот. он откроет, могут глючить так, как N одноклиентских серверов, а может, еще и больше. Оказалось, библиотека сокетов ориентирована на создание многоклиентских серверов, событийно-ориентированных на неблокирующих сокетах. Мне это не подходит. Нужна стабильная работа 1 канала связи. Вот и все. Неужели я так много захотел? А, между прочим, пришлось около 2 нед. создавать этоу логику, кот. реализована сейчас. И она работает. Но неужели нужно было так сложно обходить все эти проблемы? Вот у меня и возникли вопросы: может, я что-то не так делаю; может, можно проще и надежнее; может, это неправильный способ, или дело в том, что сама реализация логики в библиотеке сокетов не позволяет по-другому достичь желаемого результата; или я чего-то недопонял?

Насчет стресс-теста. Приложение в таком режиме работать не будет. Но, я как программист, должен был удостовериться в его надежной работе. И тайм-ауты у меня в реальном (а не тестовом) варианте составляют 2 секунды. Думаю, это не так мало. Причем, обсуждаемые проблемы проявляются по-одиночке за 5-7 часов работы сервера. Но проявляются. Хотя, с помощью описанных хитростей и обратной связи мне удалось свести потери на нет.


 
Eraser ©   (2006-09-09 21:13) [21]

> [20] _Sergey_ ©   (09.09.06 14:41)

мысль понятна, теперь скажите примерный масштаб приложения, т.е. сколько планируется в "одновременной" работе клиентов и какова нагрузка на сервер от одного клиента (в количестве скаченных/закаченных байт)?


 
_Sergey_ ©   (2006-09-09 23:10) [22]

В одновременной работе планируется:
1 ПК: сервер и клиент
+ около 10 ПК (на каждый - по клиенту) [локальная сеть небольших размеров]

Клиенты асинхронно, но довольно долго формируют пакеты. Размер 1-го пакета:
до 16 Кб максимум. Сейчас - 256 байт.


 
Eraser ©   (2006-09-09 23:13) [23]

> [22] _Sergey_ ©   (09.09.06 23:10)

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

PS как часто клиенты "обращаются" (устанавливают соединение) с сервером?


 
_Sergey_ ©   (2006-09-09 23:14) [24]

Сервер отсылает каждому клиенту клиенту только пакет с подтверждением об отправке (если успешна). Размер - 7 байт.


 
_Sergey_ ©   (2006-09-09 23:18) [25]

Частота зависит от процедуры формирования пакета. Но, чтобы клиент не занимался все время передачей пакетов, а больше времени тратил на его формирование, частота устанавливается не меньше 15 мин./клиент.

Критической секцией можно защитить. Но это не защитит от потери пакетов. А их терять нельзя, или нежелательно.


 
Eraser ©   (2006-09-09 23:18) [26]

> [24] _Sergey_ ©   (09.09.06 23:14)

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

почему не устраивает вариант с простой защитой кода от доступа из нескольких потоков? imho в данном случае - самое оно, если уж отказались от многопоточной архитектуры..


 
Eraser ©   (2006-09-09 23:21) [27]

> [25] _Sergey_ ©   (09.09.06 23:18)


> частота устанавливается не меньше 15 мин./клиент.

понятно, тоже очень мало.

> Но это не защитит от потери пакетов.

если уж так критична защита от неудачных соединений (думаю в нашем контексе это уместнее называть так, чем "потеря пакетов"), то нужно предусмотреть защиту против этого на прикладном уровне, но никак не на транспортном!


 
_Sergey_ ©   (2006-09-09 23:23) [28]

1 раз в 15 мин. (может, больше 15 мин.) при формировании пакета
через каждые 2 сек при занятости сервера

Критическая секция не защитит от записи пакета от какого-то клиента в буфер сокета, причем, получается, что именно слушающего. И на стороне сервера я эту ситуацию никак не отслежу. Выход: придется высылать клиентам пакет с разрешением на передачу. И только после получения разрешения клиент должен передавать пакет. Но, как мне кажется, введение еще 1 обратной связи - это не оч. красивый выход из этой ситуации.


 
Eraser ©   (2006-09-09 23:28) [29]

> [28] _Sergey_ ©   (09.09.06 23:23)


> Критическая секция не защитит от записи пакета от какого-
> то клиента в буфер сокета, причем, получается, что именно
> слушающего.

стоп-стоп! так и пусть пишет в этот буфер! при чем тут уже соединенный клиент? для него задействован уже совершенно другой сокет.

еще одно. когда на сервере формируется пакет для определенного клиента (как я понял это довольно длительный процесс) могут ли другие клиенты получать свои пакеты?


 
_Sergey_ ©   (2006-09-09 23:33) [30]

Ваш совет, как я понял, таков: при существующей обратной связи с подтверждением об успешной отправке ввести обратную связь перед отправкой, свидетельствующей, что соединение с сервером установлено, и он готов получать пакет.

Нет. Не на сервере формируется пакет. Это клиенты формируют пакеты длительное время. А сервер эти пакеты только получает, и быстренько их обрабатывает, после чего опять создает слушающий сокет (т.е., снова готов к работе).


 
Eraser ©   (2006-09-09 23:34) [31]

PS чтобы данные не уходили "в никуда" самый простой и применяемый способ проверки "живости" сервера - считать с него 1 байт данных, это вполне нормальный способ.


 
Eraser ©   (2006-09-09 23:34) [32]

> [30] _Sergey_ ©   (09.09.06 23:33)
> Ваш совет, как я понял, таков: при существующей обратной
> связи с подтверждением об успешной отправке ввести обратную
> связь перед отправкой, свидетельствующей, что соединение
> с сервером установлено, и он готов получать пакет.

именно!


 
_Sergey_ ©   (2006-09-09 23:40) [33]

Хорошо. Это тоже вариант, причем более корректный с точки зрения работы с сокетами. Спасибо. Буду думать, тестировать ...


 
Eraser ©   (2006-09-09 23:44) [34]

> [33] _Sergey_ ©   (09.09.06 23:40)

в догонку:
при малых объемах информации и частой смене направления передачи данных, как в данном случае, и если критична скорость реагирования, можно отключить алгоритм Нагеля, для сокетов.
см. setsockopt + TCP_NODELAY.


 
_Sergey_ ©   (2006-09-09 23:46) [35]

Понял. Спасибо. Кстати, я устанавливаю для клиентов размер буфера для отправки в 0. Чтобы данные сразу уходили в виде пакетов в сеть.


 
Eraser ©   (2006-09-09 23:48) [36]

> [35] _Sergey_ ©   (09.09.06 23:46)

когда то тоже примерно так поступал - делал размер прикладного пакета чуть больше tcp пакета, пока не узнал про TCP_NODELAY ;)
правильнее [34] :)


 
_Sergey_ ©   (2006-09-09 23:55) [37]

Хорошо. Все понятно, придется где-то пятую часть кода сервера вырезать и переписать ;) Но, думаю, это к лучшему. Более правильная реализация стоит этого.

В любом случае о результатах сообщу в эту ветку. Если все хорошо, недельки через 1,5 максимум.

Удачи.
P.S> Если есть еще какие вопросы относительно мого приложения, с радостью отвечу.


 
_Sergey_ ©   (2006-09-09 23:58) [38]

Хотя, в такой реализации сервер будет менее защищен от "неправильных" клиентов, поскольку можно будет просто подключиться, и не посылать ни одного байта. Тогда сервер будет занят таймаут мс, после чего отключит такого клиента, но всегда же можно переподключиться. Моя же реализация иребует меньше времени на таких клиентов.


 
_Sergey_ ©   (2006-09-10 00:04) [39]

Да, последний мой пост неверен. При подключении клиента сервер просто сразу же посылает ему сообщение. Потом ждет данных, после прихода маркера окончания пакета данных отсылает подтверждение об успешной отправке и разрывает соединение. По кол-ву таймаутов сервера эти две реализации не отличаются.


 
Eraser ©   (2006-09-10 00:05) [40]

> [38] _Sergey_ ©   (09.09.06 23:58)


> Хотя, в такой реализации сервер будет менее защищен от "неправильных"
> клиентов, поскольку можно будет просто подключиться, и не
> посылать ни одного байта

это легко обойти такой схемой:
1. Клиент подключается к серверу и отсылает ему 1 байт данных.
2. Сервер пытается считать из сокета 1 байт, если не получается - закрывает сокет, если получается - входит в критическую секцию.
3. Сервер отсылает 1 байт клиенту.
4. Клиент пытается считать 1 байт, если получается - отсылает пакет, если нет - убивает сокет.
5. Сервер принимает пакет и закрывает критическую секцию.

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


 
_Sergey_ ©   (2006-09-10 00:35) [41]

Конечно, сгодится. Но можно не 1 байт, а 7 или больше, для надежности (зафлудить будет тяжелее, т.к. если клиентом будет посылаться непрерывный поток данных, то вероятность встретить там исходную 7-байтную комбинацию оч. мала, а, если сервер получает не ее, можно сразу же серверу закрыть сокет).

И критическая секция не понадобится. Accept вызовется только после окончания обработки данных от текущего клиента. Всего у меня 1 поток. А до этого момента будет существовать только 1 канал связи.


 
Eraser ©   (2006-09-10 15:47) [42]

> [41] _Sergey_ ©   (10.09.06 00:35)


> зафлудить будет тяжелее, т.к. если клиентом будет посылаться
> непрерывный поток данных, то вероятность встретить там исходную
> 7-байтную комбинацию оч. мала, а, если сервер получает не
> ее, можно сразу же серверу закрыть сокет

от флуда и других злоумышленных атак данный метод не пойдет. если нужна настоящая защита - должен быть реализован настоящий анти-флуд, со списком "черных" IP, контролем от переполнения буффера, защитой от DOS-атак и т.п.


 
_Sergey_ ©   (2006-09-10 23:59) [43]

Со списком "черных" IP все понятно - SO_CONDITIONAL_ACCEPT, WSAAccept(), в процедуре - CF_REJECT. Контроль от переполнения буффера, как я понял - StrLCat (используется для "склейки" приходящих пакетов). А вот как защититься от DOS-атак? Шифровать пакеты, дак это не то. Может, занесение в черный список при оч. частых коннектах с определенного адреса, или большом числе "неправильных" пакетов с этого адреса. Было бы полезно знать. А сейчас реализовал предложенную логику, но только по более простой схеме (пока пренебрег безопасностью). Логика такова:
1) accept-им клиента
2) сразу же высылаем ему пакет с подтверждением об разрешении отправки данных
3) если клиенту не приходит данное разрешение за тайм-аут сек., то он уничтожает сокет, создает его заново и пытается законнектиться по тайм-ауту. Иначе (разрешение пришло) он отсылает данные.
4) Сервер принимает данные по тайм-ауту. Как только сервер приймет маркер окончания пакета данных, он высылает клиенту пакет с подтверждением об успешной доставке данных. При ошибке send подтверждения обработка пакета отменяется (защита от дублирования пакетов). Сервер закрывает сокет клиента и вызывает процедуру обработки принятого пакета. После чего снова переходит на Accept.
5) клиент принимает тайм-аут сек. пакет с подтверждением. Если он не получит этот пакет, то уничтожит сокет, создаст его заново, подключится и снова будет пытаться послать старый пакет через тайм-аут сек. Иначе (получил подтверждение) клиент закрывает сокет и вызывает процедуру формирования нового пакета. И снова повторяет цикл.

Отключил алгоритм Нагеля на стороне клиента. На стороне сервера после каждого accept отключаю этот алгоритм для сокета, связанного с клиентским.

Размер очереди в Listen заменил с максимальной до 1.

Кроме того, кажется нашел ответ на свой 2-ой вопрос, заданный в [0] ;)
Как показал эксперимент, слушающий сокет закрывается не сразу, поэтому к нему еще можно подконнектиться. Кажется так.

Нашел опасную описку. "Зависание" некот. клиентов, описанное выше, оказывается, было вызвано тем, что я отсылал пакет 0-го размера для проверки занятости сервера, используя конструкцию вида:
while send(...) = SOCKET_ERROR do goto reconnect;
На самом деле нужно было
if send(...) = SOCKET_ERROR then goto reconnect;

Кроме того, WSAAccept вклинивается в станд. обмен запрсами и ответами на транспортном уровне. И может создавать, как мне кажется, разл. нестандартные тайм-ауты и ситуации. Хотя, может быть, я здесь и ошибаюсь.

Результаты:
1) скорость работы 15 клиентов (тайм-ауты на переконнект - 2 сек., время формирования 1 пакета - меньше 0,5 сек) и 1 сервера, тестируемые на 1 машине через loopback, субъективно увеличилось где-то в 2 раза по сравнению с предыд. реализацией (стресс-тест, в реальности в таком режиме сервер работать не будет). [алгоритм Нагеля]
2) При анализе логов после стресс-теста обнаружилось почти полное отсутствие сообщений об занятости сервера [алгоритм Нагеля]
3) Исчезли те самые 2000 потерь пакетов на 20000 передач, описываемые выше. Раньше я просто исправлял эту ситуацию, перепередавая пакеты. Сейчас этого делать не нужно. [обратная связь + ликвидация_описки]
4) на машине W2k Pro SP4 с интегр. сетевой картой ранее были такие проблемы: после ~ 30 мин. работы винда вылетала с синим экраном, самопроизвольно перезагружаясь (автоперезагрузка при сбое отключена).
Причину по коду ошибки выяснить не удалось. При конфигурации: P4 3.0 HT, WinXP SP1, интегр. сет. такого не было. При текущей реализации сервер проработал на 1-ой машинке 5 часов без синих экранов. [слушающий сокет не создается/уничтожается с оч. большой частотой]
5) осталась проблемка: с включенным HT при стресс-тесте иногда клиенты вылетают с сообщением об ошибке обращения к памяти, адреса все время разные. При выключенном HT такая проблема исчезает. Вероятная причина: винда или ее библиотеки (включая сетевые) не всегда правильно работает на многопроцессорной машинке (процессоры хоть и виртуальные, но их все же формально 2 шт.). В старой реализации такая прблема проявлялась раза в 4 чаще. Сейчас - реже. Считаю это не проблемой моего приложения.

Т.е., улучшения налицо. Еще раз спасибо за подсказку. Оказалось, я искал решения на транспортном уровне. А надо было, как делают все протоколы, защищаться обратной связью с отправкой пакетов со служебными сообщениями.


 
Eraser ©   (2006-09-11 01:45) [44]

> [43] _Sergey_ ©   (10.09.06 23:59)


> А вот как защититься от DOS-атак?

если за опр. промежуток времени произошло опр. количесвто неудачных соединений с конкретного IP - заносить этот IP в черный список.. на опр. промежуток времени :)
хотя если нужна настоящая защита - при каждом соединений необходимо производить сначала сеанс аутентификации, потом сеанс авторизации и уже по защищенному каналу обмениваться данными. Либо производить аутентификацию/авторизацию при первой попытке связи клиента с сервером, а в дальнейшем уже использовать ранее сгенерированный ключ для шифрования данных. Второй вариант в вашем случае предпочтителен, т.к. генерация ключей с надежной криптоустойчиваостью - процесс небыстрый.. несколько секунд тратится.

> осталась проблемка: с включенным HT при стресс-тесте иногда
> клиенты вылетают с сообщением об ошибке обращения к памяти,
> адреса все время разные.

возможно в данном случае поможет ф-ия SetThreadAffinityMask, которая позволяет "привязать" поток к определенному процессору.. не знаю как оно будет работать с HT.. думаю нормально )

PS попробуйте использовать какой-нибудь готовый сервер, например TIdTCPServer и сравните скорость работы с вашей реализацией, возможно не стОит усложнять себе жизнь изобретая велосипеды, хотя может быть тут я не прав.


 
Сергей М. ©   (2006-09-11 08:47) [45]


> _Sergey_ ©   (09.09.06 00:16) [16]


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

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


 
_Sergey_ ©   (2006-09-11 13:09) [46]


> PS попробуйте использовать какой-нибудь готовый сервер,
> например TIdTCPServer и сравните скорость работы с вашей
> реализацией, возможно не стОит усложнять себе жизнь изобретая
> велосипеды, хотя может быть тут я не прав.


В том-то и дело, что до того, как реализовывать сетевую логику работы на ф-иях библиотеки сокетов, я пробовал реализовать ее же с помощью различных компонентов:
1) стандартные TClientSocket, TServerSocket. Много "глюков": образование незакрытых соединений при активном тестировании, нет контроля над кол-вом создаваемых потоков (каналов связи), иногда выдают разные сообщения об ошибках в виде MessageBox-ов, при подключении Telnet-ом не определяет, что к серверу кто-то подключился, кроме того, при приеме данных ждет прихода хотя бы 1-го байта (причем, может ждать и 5 мин., и час, и два ...)...
2) Indy. Реализован полностью на блокирующих сокетах, даже есть св-во max числа подключений. Но: при подключении Telnet-ом не определяет, что к серверу кто-то подключился, кроме того, при приеме данных ждет прихода хотя бы 1-го байта (причем, может ждать и 5 мин., и час, и два ...); не понравилась инсталляция Indy 10 на D7 (инсталлятор даже не смог правильно подключить 10 версию, хотя он был специально предназначен для D7); не удалось отключать клиента внутри таймера, т.к. управление объектом, идентифицирующем соединение, реализуется только внутри процедуры обработки прихождения данных от клиента (название процедуры точно не помню), в Indy 10 была частично перестроена иерархия св-в для объектов и изменена сама иерархия объектов, что решило эту проблему ...
3) Пишу сейчас не из домашней машинки. Название не могу глянуть, но пробовал еще 1 пакет компонентов. Коммерческий продукт, реализации существуют для Borland C++ Builder, Borland Delphi и кажется, Kylix, для разных версий. Отдельно были компоненты, поддерживающие SSL. Но там не было св-ва, ограничивающего число подключений. Хотя, при подключении к такому серверному компоненту telnet-ом, он определил факт подключения, чего не смогли сделать компоненты, описанные в 1) и 2).

Поэтому решил написать сам. На более низком уровне, чем использование компонентов. Ниже - только работа с драйверами и написание своих драйверов. В такие дебри пускаться я не хочу. И теперь, мое приложение будет иметь баги MS (библиотека сокетов), Borland (компилятор), и мои. И никак не баги разработчиков компонентов. Кроме того, я могу настроить св-ва (адаптировать работу сокетов под свои нужды) и контролировать каждое соединение.

P.S> Когда я скачал документацию с оф. сайта по Indy 10 в *.html (запакованные), я ужаснулся. Если там код находится в таком же состоянии, как и документация, то я удивляюсь, как он вообще работает. Кто видел эту документацию, поймет меня.


 
_Sergey_ ©   (2006-09-11 13:26) [47]


> хотя если нужна настоящая защита - при каждом соединений
> необходимо производить сначала сеанс аутентификации, потом
> сеанс авторизации и уже по защищенному каналу обмениваться
> данными. Либо производить аутентификацию/авторизацию при
> первой попытке связи клиента с сервером, а в дальнейшем
> уже использовать ранее сгенерированный ключ для шифрования
> данных. Второй вариант в вашем случае предпочтителен, т.
> к. генерация ключей с надежной криптоустойчиваостью - процесс
> небыстрый.. несколько секунд тратится.


Т.е., SSL, или шифрование. На сегодняшний день нашел только 1 компонент, позволяющий шифровать данные, но он визуальный, и требует наличия формы. Я же хочу оставить свое приложение консольным. Сам написать модуль, обеспечивающий должный уровень шифрования, я не в силах. Готового не нашел. Могу реализовать что-то простенькое, что защитит только "от дурака".
Есть пример связывания Indy 9 (10) с библиотекой OpenSSL. Единственной библиотекой, как я понял, доступной для связывания с Delphi (с предварительной небольшой адаптацией). Но этот пример, как мне кажется, работает как-то "неправильно". В самом примере при переведении в активное состояние idTcpServer, связанного с OpenSSL, введена задержка по таймеру. Если эту задержку убрать, то возникают разного рода ошибки. Кроме того, при переведении этого же idTcpServer в неактивное состояние приложение с idTCPServer-ом "вываливается" по Access violation ... Чьи это проблемы - Indy или OpenSSL, я не знаю. Но проблемы налицо. Кроме того, я предполагаю, что будет не так-то просто связать консольное приложение на Delphi с OpenSSL, поскольку нужно написать некий промежуточный код, который позволил бы свести взаимодействие с библиотекой SSL к простому вызову процедур для открытия сертификата ... Есть еще вариант - 3 компонент. Но не хочу я использовать сторонние компоненты. Хотя, есть серьезные предположения, что как раз этот компонент реализован довольно "качественно". Что Вы можете посоветовать из числа готовых программных пакетов для применения с Delphi (консольное приложение) для шифрования или создания защищенных линий связи?


 
Сергей М. ©   (2006-09-11 13:30) [48]


> 1) стандартные TClientSocket, TServerSocket. Много "глюков":
>  образование незакрытых соединений


Это не "глюки". Это как минимум незнание особенностей реализации компонентов и/или нежелание читать их исх.тексты, благо они всегда имеются.


> нет контроля над кол-вом создаваемых потоков (каналов связи)


Есть такой контроль. Вполне успешно работающий.


> выдают разные сообщения об ошибках в виде MessageBox-ов


По-твоему, возникающие ошибки должны "замалчиваться" компонентами, так что ли ?


> при подключении Telnet-ом не определяет, что к серверу кто-
> то подключился


Вот уж глупости. И причем здесь вообще Telnet ? Компоненты эти реализуют транспортный уровень, ни больше ни меньше. Прикладной же уровень в этом случае целиком и полностью на твоей совести.


> при приеме данных ждет прихода хотя бы 1-го байта (причем,
>  может ждать и 5 мин., и час, и два


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

В блокирующем же режиме никто не запрещает воспользоваться select"ом для работы с транспортом по таймаутам.


> И никак не баги разработчиков компонентов


Нет там никаких сколь-либо серьъезных "багов".
А если и есть - на то имеются исх.тексты.


 
Rouse_ ©   (2006-09-11 13:31) [49]

Слушай, возьми ICS и не мучайся, давным давно Пиетта за нас все написал, ни убавить ни добавить (правда не весь функционал, но все-же) :)


 
_Sergey_ ©   (2006-09-11 13:33) [50]


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


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


 
Сергей М. ©   (2006-09-11 13:56) [51]


> последовательная обработка позволяет не "заморачиваться"
> с синхронизацией потоков, критическими секциями


Это никак не вяжется с изначальным вопросом.
Берешь обычные TServer/ClientSocket или TTCPServer/Client и используешь их в неблок.режиме - вот тебе и послед.обработка и гарантированная доставка сообщений.


> однопотоковый сервер в состоянии обслужить всех клиентов


TServerSocket/TTCPServer в неблок.режиме как раз и реализуют однопоточный серверный транспорт.
И не понятно, к чему ты при этом упомянул TIdTCPServer - он заведомо многопоточный.


 
Eraser ©   (2006-09-11 16:06) [52]

> [50] _Sergey_ ©   (11.09.06 13:33)


> В данном случае последовательная обработка позволяет не
> "заморачиваться" с синхронизацией потоков, критическими
> секциями, позволяет не обходить тупиковые ситуации (когда
> один процесс заблокировал какой-то ресурс), поскольку и
> однопотоковый сервер в состоянии обслужить всех клиентов.

ну тут уже не раз повторили, что это ерунда, то что та пишешь.

какие проблемы с крит. секцией? 2 строчки кода (ну пусть 5, с учетом try..finally..end) это проблемы?
а вот реализация полностью однопоточного сервера, в том виде, что ты хочешь - вот это проблема.

насчет компонентов - согласен с Сергеем М., в таких базовых компонентах как TServer/ClientSocket или TIdTCPServer я глюков не встречал, они имеются в более сложных реализациях, но в этих компонентах - вряд ли.

> при приеме данных ждет прихода хотя бы 1-го байта (причем,
> может ждать и 5 мин., и час, и два ...);

см. свойство ReadTimeout.

> понравилась инсталляция Indy 10 на D7 (инсталлятор даже
> не смог правильно подключить 10 версию, хотя он был специально
> предназначен для D7

опять же - просто надо уметь готовить :) у меня проблем с инсталляцией не возникало.

> P.S> Когда я скачал документацию с оф. сайта по Indy 10
> в *.html (запакованные), я ужаснулся. Если там код находится
> в таком же состоянии, как и документация, то я удивляюсь,
> как он вообще работает. Кто видел эту документацию, поймет
> меня.

не знаю как на сайте, их chm файл меня вполне устраивал, сейчас правда пользуюсь встроенной справкой из BDS2006.

> Т.е., SSL, или шифрование.

не обязательно. я имел ввиду свою реализацию, к примеру, на базе MS Crypto API, что не так уж и трудно.


 
_Sergey_ ©   (2006-09-12 00:17) [53]


> TServerSocket/TTCPServer в неблок.режиме как раз и реализуют
> однопоточный серверный транспорт.

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

IdTCPServer - компонент, сетевая логика которого основана на блок. сокетах. Вот к тому и упоминаю. А еще там св-во есть, MaxConnections называется. Оч. полезное св-во.


 
Eraser ©   (2006-09-12 00:36) [54]

> [53] _Sergey_ ©   (12.09.06 00:17)


> IdTCPServer - компонент, сетевая логика которого основана
> на блок. сокетах.

Эт я в курсе :)
его и советую использовать.


 
_Sergey_ ©   (2006-09-12 00:36) [55]


> Есть такой контроль. Вполне успешно работающий.

В свое время не нашел, хотя долго искал. Может, подскажете, как именно реализуется этот контроль?


> По-твоему, возникающие ошибки должны "замалчиваться" компонентами,
> так что ли ?

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


> Вот уж глупости. И причем здесь вообще Telnet ? Компоненты эти
> реализуют транспортный уровень, ни больше ни меньше. Прикладной же
> уровень в этом случае целиком и полностью на твоей совести.

Не вижу здесь глупости. Прикладной уровень то на моей совести. Но как я смогу отключить клиента, если я даже не знаю, что кто-то уже подключился к серверу. К примеру, в idTCPServer поставлю MaxConnections=1. Вызвать DOS для такого сервера проще не бывает - подключиться к нему телнетом. А тогда на совести, не на совести, а вся логика прикладного уровня не будет иметь никакого смысла, ведь сервер даже не будет знать, что единственный канал связи уже занят.


 
_Sergey_ ©   (2006-09-12 00:51) [56]


> Eraser © [54]

Боюсь, назад дороги нет :)
Можно сказать, что сейчас я доволен реализацией и работой. Может, следует попытаться защитить и усложнить обработку, но на первых порах неплохо.


 
_Sergey_ ©   (2006-09-12 00:53) [57]


> Rouse_ © [49]

Что такое ICS?


 
Eraser ©   (2006-09-12 01:21) [58]

> [56] _Sergey_ ©   (12.09.06 00:51)


> К примеру, в idTCPServer поставлю MaxConnections=1.

ну сколько ж раз говорить - не нужно ограничевать количество клиентов на транспортном уровне.. кроме доп. нагрузке на сеть и увеличению кол-ва ошибок это ни к чему не приведет.

> Что такое ICS?

компоненты, основанные на неблокирующих сокетах.

> Боюсь, назад дороги нет :)

код сервера:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer,
 IdContext, IdGlobal;

type
 TfmMain = class(TForm)
   IdTCPServer1: TIdTCPServer;
   procedure IdTCPServer1Execute(AContext: TIdContext);
   procedure FormCreate(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 fmMain: TfmMain;
 csSync: RTL_CRITICAL_SECTION;

implementation

{$R *.dfm}

procedure TfmMain.FormCreate(Sender: TObject);
begin
 InitializeCriticalSection(csSync);
 IdTCPServer1.DefaultPort := 777;
 IdTCPServer1.Active := true;
end;

procedure TfmMain.IdTCPServer1Execute(AContext: TIdContext);
var
 Buf: TBytes;
 BufSize: Integer;
begin
 AContext.Connection.IOHandler.Write(Integer(1));
 AContext.Connection.IOHandler.ReadInteger; // Dummy value.
 try
   EnterCriticalSection(csSync);
   BufSize := AContext.Connection.IOHandler.ReadInteger; // Размер пакета.
   AContext.Connection.IOHandler.ReadBytes(Buf, BufSize); // Принимаем буффер.
 finally
   LeaveCriticalSection(csSync);
 end;
end;

end.

код клиента:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
 IdGlobal;

type
 TfmMain = class(TForm)
   procedure FormCreate(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 fmMain: TfmMain;

implementation

{$R *.dfm}

procedure TfmMain.FormCreate(Sender: TObject);
var
 IdTCPClient: TIdTCPClient;
 buf: TBytes;
begin
 SetLength(buf, 100);  // Устанавливаем длину пакета.
 buf[0] := 77; // Инициализируем пакет.
 IdTCPClient := TIdTCPClient.Create(Application);
 try
   IdTCPClient.Host := "127.0.0.1";
   IdTCPClient.Port := 777;
   IdTCPClient.Connect;
   IdTCPClient.IOHandler.ReadInteger;
   IdTCPClient.IOHandler.Write(Integer(1));
   IdTCPClient.IOHandler.Write(Integer(Length(buf))); // Отсылаем размер буффера.
   IdTCPClient.IOHandler.Write(buf); // Отсылаем буффер.
   IdTCPClient.Disconnect;
 finally
   IdTCPClient.Free;
 end;
end;

end.

пример на Indy 10, встроенного в BDS2006.


 
Сергей М. ©   (2006-09-12 08:56) [59]


> _Sergey_ ©   (12.09.06 00:17) [53]
> Это компоненты, создающие в неблок. режиме столько подключений,
>  сколько клиентов попытается подключиться. В блок. режиме
> - другое дело.


Дались тебе эти "лишние" подключения !)

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

И хоть в блокирующем хоть в неблокирующем режиме все это происходит - нет никакой разницы.


> А еще там св-во есть, MaxConnections называется. Оч. полезное
> св-во


Ничего полезного оно для тебя не представляет. Работатет оно точно так же примитивно - если очередной подключившийся клиент "лишний", для него тут же выполняется дисконнект.


> подскажете, как именно реализуется этот контроль?


см. TServerSocket.ActiveConnections


> программа должна уметь правильно их обработать, оставляя
> за собой способность работать в автоматическом режиме, а
> не ждать нажатия пользователя на кнопку очередного MessageBox-
> а с ошибкой


Вот и обрабатывай их правильно)..  в чем проблема-то ?

По поводу DoS-атак, если они так тебя волнуют, ты уж выбирай - либо настоящий полноценный сервер, реализующий true conditional acceptance, либо его жалкое примитивное подобие, опирающееся на backlog queue size = 1


 
_Sergey_ ©   (2006-09-12 17:05) [60]


> код сервера:

Ээх. Спасибо за пример, но назад я возвращаться не буду.


> Насколько я понял, у тебя одна проблема - ты не в состоянии отладить
> сервер, работающий одновременно более чем с одним клиентом. Но это
> же не повод извращаться с ограничением кол-ва клиентов до единицы !

Может, действительно не в состоянии. Я могу это признать. Я любитель, а не профессионал. Тем более, занимаюсь программированием для сетей 3 месяца с перерывами. Сервер, работающий с 1 client max - это простое решение. И его с головой хватит для моей задачи. В жестких условиях тестирования он себя хорошо показал. В таких условиях, как при тесте, он небудет работать никогда. Тем не менее, и при тесте он справляется. Это не сервер, предназначенный для передачи файлов.

> см. TServerSocket.ActiveConnections

TServerSocket.Socket.ActiveConnections - число законнекченных клиентов
При его превышении я могу только вручную (через св-ва) отконнектить лишних. А это не годится. Лишние клиенты совсем не должны коннектиться.

> либо настоящий полноценный сервер, реализующий ...

Думаю, меня и текущая реализация вполне устраивает. Она имеет запас по производительности и надежность. Защита, правда, минимальна. Но, если мое приложение и будет использоваться, то только в изолированной ЛС, где никто не будет пытаться навредить его работе (подделывать пакеты для отправки, досить, ...). Мои вопросы по реализации безопасности - это вопросы на будущее. С уклоном: авось пригодится; а вдруг придется реализовывать. А пока меня больше волнуют вопросы обеспечения совместмости с HT. Буду пробовать использовать SetThreadAffinityMask
.


 
Eraser ©   (2006-09-12 17:16) [61]

> [60] _Sergey_ ©   (12.09.06 17:05)


> Сервер, работающий с 1 client max - это простое решение.

это, уж извини, туповатое решение. зачем ограничевать количество соединенй?.. ума не приложу...
мало того, что это "решение" уменьшает производительность системы и увеличивает нагрузку на процессор и сеть, оно ещё и трудно реализуемо, в том виде, в котором ты хочешь.. а самое главно - бесполезно.


 
_Sergey_ ©   (2006-09-12 22:11) [62]


> зачем ограничивать количество соединений

Все начинается с малого. Пример: 100 кг груз можно перевести легковой машиной и грузовиком. Но 20 т легковой машиной не перевезти. Когда мне понадобится перевезти 20 т, я возьму грузовик: по моим расчетам мой сервер (теоретически, на основе подсчета числа обслуженных клиентов при стресс-тесте) уже, при одноклиентской реализации, сможет обслужить около 20 тыс. клиентов за примерно 30 мин. на P4. Это идеальные условия. Сбросим 3 тыс. на перепередачу при занятости сервера. И того: 17 тыс. за 30 мин. в ЛС. А если учесть, что каждый из клиентов будет передавать небольшой пакет через примерно 15 мин., получится, что половина из 17 тыс. - это и есть число работающих клиентов (отдельных машин, или задействованных процессоров при использовании многопроцессорных машин [на них запускается столько клиентов, сколько на них установлено процессоров]). И того получается оч. большая цифра: 8 500 клиентов. Такое число клиентов не планируется, и его не будет никогда! Максимум - 10. Вопрос: в чем ограниченность моего мышления? Если и так запас по производительности по теоретическим оценкам превосходит ожидаемое число клиентов в 850 раз. Все просто.


> это "решение" уменьшает производительность системы

Реконнекты клиентов не повышают нагрузку на сервер. Разве что в те несколько секунд реконнектов клиента загружают процессор, на котором работает клиент. И то я в клиенте использую при реконнекте sleep(2000), получается, я не столько загружаю процессор на клиентской стороне, сколько теряю процессорное время на стороне клиента. Но что такое пару секунд по сравнению с 15 минутами?
Тем более, даже в стресс-тесте реконнект клиентов в текущей реализации - единичные случаи. На около 2000 передач от клиентов у меня было 20-50 повторных передач из-за занятости сервера. Могу протестировать еще раз и выслать логи клиента на мыло, если интересно. Думаю, у мнея не самые плохо реализованные логи :) Причем, и серверные тоже могу выслать. Но там будет несколько Мб подобного текста, но все события довольно подробно описаны.

> увеличивает нагрузку на ... сеть

Работать приложение будет в ЛВС. Если и будут повторные передачи, то их будет оч. мало, судя по стресс-тесту. Скорость там достаточная, и небольшим количеством "лишних" маленьких пакетов можно пренебречь, как мне думается.


> трудно реализуемо

Уже реализовал. Благодаря твоей же
подсказке. Как я сразу сам не додумался ввести обратную связь перед передачей, ума не приложу.


> бесполезно

Если еще не убедил, то скажу так. Win 9x плохо переносят многопоточные приложения (заявление из собственного опыта и высказываний людей на разных форумах, в частности, сетевые многопоточные приложения, прекрасно работающие на WinNT, работают и на Win 9x, но периодически "вылетают" с разными сообщениями об ошибках. Это только один из примеров.). Для меня важна совместимость, начиная по крайней мере с W98.
Понятно, что достаточно хотя бы 1 машинки под WinNT, и можно подумать и о
conditional acceptance, и об других, специфичных для WinNT 4.0+ WinSock 2.2 Windows-расширениях ф-ий работы с сокетами. Но могу сказать так:
Если все же производительности сервера мне не будет хватать->если повторных передач будет слишком много->если клиенты будут простаивать долго, то я сделаю так:
вынесу код работы с клиентом (после accept) в отдельную процедуру, защищу код обработки пакетов критической секцией, и буду создавать отдельные потоки на каждое соединение, но ограничу максимальное число потоков, скажем, 30-ю. Таким образом, у меня получится многоклиентский сервер на блокирующих сокетах (не самая удачная реализация, но ее будет довольно). При этом уменьшится время реакции сервера (клиент почти гарантированно будет передавать пакеты сразу), но при зависании хотя бы 1 потока в этом случае и не выхода из критической секции все ост. потоки будут блокированы. Сервер при этом будет в нерабочем состоянии. Время реакции сервера уменьшится, но не увеличится скорость обработки пакетов, поскольку в моей реализации сейчас: если сервер не создает канал связи с очередным клиентом, значит, он в этот момент обрабатывает данные, пришедшие от предыдущего клиента, или принимает эти данные. А все эти процедуры оч. быстрые. Таким образом, выиграш в производительности ожидается мизерный (на основе информации, изложенной выше). Вопрос: а стоит ли гнаться за сомнительными меньше 1% приростами в задачах, когда этот прирост будет проявлять себя лишь при нагрузках, больших в 850 раз, чем планируемые нагрузки?


 
Eraser ©   (2006-09-13 01:43) [63]

> [62] _Sergey_ ©   (12.09.06 22:11)


> Максимум - 10. Вопрос: в чем ограниченность моего мышления?

в этом

> Такое число клиентов не планируется, и его не будет никогда!


> Реконнекты клиентов не повышают нагрузку на сервер.

повышают, установка TCP соединения - сравнительно затратный процесс.

> Уже реализовал. Благодаря твоей же
> подсказке. Как я сразу сам не додумался ввести обратную
> связь перед передачей, ума не приложу.

хм.. а при чем тут обратная связь.. я имел ввиду реализацию полностью однопоточного сервера без дисконнектов, о которых писали тут [59].

> На около 2000 передач от клиентов у меня было 20-50 повторных
> передач из-за занятости сервера.

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

> Если еще не убедил, то скажу так. Win 9x плохо переносят
> многопоточные приложения

угу, когда создешь около десятка загруженных потоков - плохо )

> в частности, сетевые многопоточные приложения, прекрасно
> работающие на WinNT, работают и на Win 9x, но периодически
> "вылетают" с разными сообщениями об ошибках

в этом виновата только кривость рук программистов, делающих программы, которые ""вылетают" с разными сообщениями об ошибках".

> Для меня важна совместимость, начиная по крайней мере с
> W98.
> Понятно, что достаточно хотя бы 1 машинки под WinNT, и можно
> подумать и о
> conditional acceptance, и об других, специфичных для WinNT
> 4.0+ WinSock 2.2 Windows-расширениях ф-ий работы с сокетами.

вот то-то и оно, кстати, если уж на то пошло - есть повод задуматься о разных версиях реаизации протокола для разных систем.. реализовать это не так уж сложно.

> Если все же производительности сервера мне не будет хватать-
> >если повторных передач будет слишком много->если клиенты
> будут простаивать долго, то я сделаю так:
> вынесу код работы с клиентом (после accept) в отдельную
> процедуру, защищу код обработки пакетов критической секцией,
> и буду создавать отдельные потоки на каждое соединение,
> но ограничу максимальное число потоков, скажем, 30-ю. Таким
> образом, у меня получится многоклиентский сервер на блокирующих
> сокетах

1.именно так и нужно делать сразу. по крайней мере, мне не понятны причины, почему так не делать.
2. почему именно на блокирующих?
3. все уже сделано до тебя, называется TIdTCPServer :)

> не самая удачная реализация, но ее будет довольно

тоже не понятно почему? собственное умозаключение?

> но при зависании хотя бы 1 потока в этом случае и не выхода
> из критической секции все ост. потоки будут блокированы.

а в твоей реализации не будут? к тому же сам код передачи данных не обязан быть защищен крит. секцией, данные можно считывать в промежуточный локальный буффер, а уже после заполнения этого буффера нужным пакетом, синхронизировать с программой, вот и все.

PS и все таки еще раз посоветую - не изобретай велосипеды, на своем опыте не раз убеждался - неблагодарное это занятие :)


 
Сергей М. ©   (2006-09-13 08:44) [64]


> _Sergey_


Подозреваю, что ты изначально пошер неверной дорогой.

Если речь идет только об ЛВС под управлением Windows, то задача "гарантия доставки + минимизации сетевой нагрузки + максимальная производительность транспорта + ограничение числа одновременно обслуживаемых сервером клиентов" с легкостью решается использованием именованых программных каналов (вместо Winsock).


 
_Sergey_ ©   (2006-09-13 11:41) [65]


> в этом

Это не ограниченность, а реальность.


> я имел ввиду реализацию полностью однопоточного сервера
> без дисконнектов

Правильно. У меня сейчас полностью однопоточный сервер без дисконнектов. Сервер не создает канал обмена данными, пока он обслуживает хотя бы 1-го клиента (для 2-го клиента просто не вызывается accept). И дисконнект он не вызывает для 2-го (N-го) клиента. 2-ой клиент добавляется в очередь на подключение, но поскольку он не получает от сервера разрешения на передачу, то он определяет, что он не первый. И повторяет попытку подключения.


> повышают, установка TCP соединения - сравнительно затратный
> процесс.

Вот как раз на сервер нагрузку и не повышают. Соединение то не устанавливается.


> 1.именно так и нужно делать сразу. по крайней мере, мне
> не понятны причины, почему так не делать.

Потому, что и 1 потока мне достаточно. Незачем создавать лишние потоки, если теоретическая производительность, расчитанная на основании теста, превышает ожидаемую более чем в 800 раз.


> 2. почему именно на блокирующих?

Идеология их мне понравилась. Простота (линейность пргграммы). Да и незачем мне вызывать сетевые ф-ии, чтобы они выполнялись в фоне. Я должен быть уверен, что ф-ия завершилась с каким-то результатом к моменту, когда программа собирается выполнить след. ф-ию или операцию. Тем более, в *NIX системах "родными" являются блокирующие сокеты. Если будет задача перенести все это на сервер, работающий под *NIX, то проблемы почти минимальны - все ф-ии для работы с сетью у меня только стандартные, не используются Windows-расширения. Останется только переписать некоторый дополнительный код, попробывать скомпилировать Kylix или FreePascal (говорят, неплохая совместимость с Delphi). На худой конец, не составит большого труда перевести на C++ под gcc. Но тут уже придется постараться (для меня).


> собственное умозаключение?

Не совсем. "Размножать" и уничтожать потоки - это довольно ресурсоемкий процесс. Поэтому в 1 поток обычно выносится работа с несколькими клиентами. А у меня будет 1 поток = 1 клиент.

"Какой вариант использовать - поток на каждый сокет, либо один поток обрабатывает все соединения - вопрос, к которому нужно подходить по-разному в каждом конкретном случае. Если в процессе общения с клиентом серверу требуется значительное время на подготовку и обработку данных, то, конечно, следует создать разные потоки, чтобы другим клиентам не пришлось ожидать завершения обработки, а работать паралелльно. В противном случае, если обработка минимальна, то создание большого количества процессов окажет влияние в худшую сторону на общую производительность системы." (c)

Теперь у меня есть 1 поток - "кусочек" многопоточного сервера, и мой сервер всегда можно переделать в многопоточный.

Остался вопрос по SetThreadAffinityMask. Если имеется многопоточный сервер, то:
1) Если NT, то определить кол-во установленных процессоров через GetSystemInfo.
2) Для каждого подкл. клиента создавать свой поток, привязывая его SetThreadAffinityMask сначала к 0, след. поток - к 1-му, ... N, затем снова 0-му процессору?


> Подозреваю, что ты изначально пошер неверной дорогой.
>
> Если речь идет только об ЛВС под управлением Windows, то
> задача ...

Почему только под управлением Windows?
http://en.wikipedia.org/wiki/Named_pipe
Pipes - это идеология изначально *NIX-систем, в кот. они используются для коммуникации между процессами.
http://www.kolasc.net.ru/cdo/programmes/os/343.htm

Для того, чтобы связать процессы на разных ПК, нужно создать Named Pipes.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ipc/base/named_pipes.asp
"Windows Me/98/95:  Named pipes cannot be created." (с)
Поэтому этот вариант не подходит.


 
_Sergey_ ©   (2006-09-13 12:04) [66]

И еще один вопрос: как передавать 0-й байт (00h). Ведь при копировании из приемного буфера этот байт расценивается как конец строки? Или его надо заменять на другую комбинацию символов? Но тогда как работают протоколы, основывающиеся на TCP/IP, и передающие бинарные файлы? Ведь в них однозначно встречаются 0-е байты.


 
Сергей М. ©   (2006-09-13 12:33) [67]


> Почему только под управлением Windows?


Потому что это твой случай, судя по перечню ОС в сабже.
В целом ничего не имею против *nix на эту тему.


> "Windows Me/98/95:  Named pipes cannot be created." (с)


Только идиоту придет мысль размещать серьезную серверную логику на маздайной платформе.
Маздай - платформа для рабочих станций, но никак не серверная)


> при копировании из приемного
> буфера этот байт расценивается как конец строки?


КЕМ (или чем) расценивается ?


 
Сергей М. ©   (2006-09-13 12:35) [68]


> как работают протоколы, основывающиеся на TCP/IP, и передающие
> бинарные файлы? Ведь в них однозначно встречаются 0-е байты


так и работают.

Самому TCP-протоколу безразлично содержимое транспортируемых с его помощью данных. Все остальное - право и обязанность прикладных надстроек над TCP.


 
Eraser ©   (2006-09-13 13:52) [69]

> [65] _Sergey_ ©   (13.09.06 11:41)


> Остался вопрос по SetThreadAffinityMask. Если имеется многопоточный
> сервер, то:
> 1) Если NT, то определить кол-во установленных процессоров
> через GetSystemInfo.
> 2) Для каждого подкл. клиента создавать свой поток, привязывая
> его SetThreadAffinityMask сначала к 0, след. поток - к 1-
> му, ... N, затем снова 0-му процессору?

можно так, можно менее экономичный вариант - взять и привязать весь процесс к опр. процессору SetProcessAffinityMask.


 
Сергей М. ©   (2006-09-13 13:59) [70]


> _Sergey_


Нафих тебе, скажи на милость, сдалась SetProcess/ThreadAffinityMask, если ты изначально планируешь однопоточность своего сервера ?


 
GanibalLector ©   (2006-09-13 17:09) [71]

Хм...я,например, сделал сабж.
Каков был подход :
-на сервере при каждом новом клиенте я создаю НОВЫЙ поток, в поток передаю некоторую структуру(в одном из полей структуры присутствует CountThread,т.е. кол-во работающих потоков).
-создавшийся поток первым делом проверяет эту структуру(а именно CountThread) и если он больше 1,то

begin
   ShutDown(Sock,1);
   CloseSocket(Sock);
 end ;

т.е. принудительно оборвать связь с клиентом.
Вот,собственно, и все. Работает сабж на Win98,WinXP.
Код пока не привожу,надо оформить и исправить некоторые ошибки :)


 
_Sergey_ ©   (2006-09-13 20:57) [72]


> Только идиоту придет мысль размещать серьезную серверную
> логику на маздайной платформе.
> Маздай - платформа для рабочих станций, но никак не серверная)

Есть провайдеры и конторы, которые используют Win2003 Server для предоставления разл. услуг. Я не буду спорить по поводу идиотизма такого решения, но это есть. Платформа для рабочих станций - это да. Сам *nix не юзаю. Но признаю его как серьезное семейство ОС, но только в отношении построения на их базе серверов, в плане удобства работы десктопные решения, основывающиеся на *nix недалеко ушли от своих серверных собратьев (ИМХО, можно тут затеять спор, но спорить мне не к чему на эту тему). Куда удобнее Win. Дак вот. ВСЕ машинки, на кот. планируется запускать клиентов, работают именно под Win, т.к. это ПК обычных рядовых пользователей. Не больше, не меньше. И никто не будет там ставить ничто другое. И с LiveCD никто загружаться не будет, как и прерывать свою работу.
Поэтому и клиент, и сервер (не обязательно, можно 1 машинку под платформой NT и найти) должен работать под управлением Win. Причем клиент - обязательно на ВСЕХ версиях Win. Поэтому pipes по постановке задачи не подходят.


> КЕМ (или чем) расценивается ?

Приемные и передающие буфера сокетов - это массивы PChar. Так уж винда сделана. Для копирования из буфера нужно применять ф-ии, работающие с PChar (null-terminated strings) - например, StrLCat я использую для объединения частей данных при recv. Но в null-terminated strings нету в 0-м байте строки, или где-либо еще счетчика кол-ва символов в строке. Конец строки "опознается" по 0-му байту. Вот и получается, что, встретив 0-й байт, ф-ия считает, что данных в буфере нет. А они там есть. Вопрос: как их оттуда "забрать" и "склеить" (append) с уже существующей строкой?


> ... сдалась SetProcess/ThreadAffinityMask, если ты изначально
> планируешь однопоточность своего сервера ?

Это был вопрос, кот. имел своей целью подтвердить/опровергнуть мои догадки по использованию SetThreadAffinityMask. Зачем мне нужно использовать эту ф-ию?

> 5) осталась проблемка: с включенным HT при стресс-тесте
> иногда клиенты вылетают с сообщением об ошибке обращения
> к памяти, адреса все время разные. При выключенном HT такая
> проблема исчезает. Вероятная причина: винда или ее библиотеки
> (включая сетевые) не всегда правильно работает на многопроцессорной
> машинке (процессоры хоть и виртуальные, но их все же формально
> 2 шт.). В старой реализации такая прблема проявлялась раза
> в 4 чаще.



> возможно в данном случае поможет ф-ия SetThreadAffinityMask,
>  которая позволяет "привязать" поток к определенному процессору.
> . не знаю как оно будет работать с HT.. думаю нормально
> )


 
_Sergey_ ©   (2006-09-13 21:04) [73]


> Хм...я,например, сделал сабж.
> Каков был подход :
> -на сервере при каждом новом клиенте я создаю НОВЫЙ поток,
>  в поток передаю некоторую структуру(в одном из полей структуры
> присутствует CountThread,т.е. кол-во работающих потоков).
>
> -создавшийся поток первым делом проверяет эту структуру(а
> именно CountThread) и если он больше 1,то
>
> begin
>    ShutDown(Sock,1);
>    CloseSocket(Sock);
>  end ;
>
> т.е. принудительно оборвать связь с клиентом.
> Вот,собственно, и все. Работает сабж на Win98,WinXP.
> Код пока не привожу,надо оформить и исправить некоторые
> ошибки :)

Спасибо. Я понял Вашу реализацию. Но я уже реализовал сабж. Причем, как я скромно полагаю, более правильным способом: я просто-напросто не вызываю accept для 2-го и последующих клиентов. И отключать мне поэтому некого, т.к. никто лишний и не подключен (сохраняются ресурсы системы, на кот. стоит сервер; да и зачем устанавливать соединение для др. клиентов, если этого и не надо, а затем отключать их?). И оба (сервер и клиент) моих приложения работают под Win 9.x и WinNT. Разве что под Win95 не проверял. Но, подозреваю, что и без обновления до WinSock 2.2 под Win95 тоже будет работать.


 
Eraser ©   (2006-09-13 22:18) [74]

> [72] _Sergey_ ©   (13.09.06 20:57)


> > Только идиоту придет мысль размещать серьезную серверную
>
> > логику на маздайной платформе.
> > Маздай - платформа для рабочих станций, но никак не серверная)
>
>
> Есть провайдеры и конторы, которые используют Win2003 Server
> для предоставления разл. услуг. Я не буду спорить по поводу
> идиотизма такого решения, но это есть.

при чем тут win2k&? маздайная платформа это win95/98/me :)


 
Сергей М. ©   (2006-09-14 08:49) [75]


> Есть провайдеры и конторы, которые используют Win2003 Server
> для предоставления разл. услуг. Я не буду спорить по поводу
> идиотизма такого решения, но это есть


"Маздаем" принято обзывать линейку ОС Win9x/Me.


> Приемные и передающие буфера сокетов - это массивы PChar.
>  Так уж винда сделана. Для копирования из буфера нужно применять
> ф-ии, работающие с PChar (null-terminated strings)


Ох и ересь ты несешь)


 
_Sergey_ ©   (2006-09-14 13:42) [76]


> Ох и ересь ты несешь)

Прочитал в книге. Если ересь, то поправьте меня, и скажите, как правильно, если не сложно конечно. Но у меня есть такая проблема, и мне нужно найти ее решение. В частности, можно применять массив char, и вручную копировать столько символов, сколько вернет recv. Но, как известно, некот. ф-ии работы со строками написаны на asm, и поэтому более быстродействующие. Поэтому предпочтительнее не копировать строки посимвольно, а применять ф-ии для работы со строками. Неужели я и в этом неправ? ;) Или, в условиях большой производительности современных ПК не имеет значения, применеяется ф-ия, написанная на чистом asm, или на языке высокого уровня?


 
Сергей М. ©   (2006-09-14 13:59) [77]


> _Sergey_ ©   (14.09.06 13:42) [76]


Чем дальше в лес, тем больше дров)
Начали за здравие - ограничение числа одновременно обслуживаемых клиентов, а продолжили за упокой - работа со строковыми данными.

Ну никак не связаны эти две темы !

Ну хорошо, поговорим "за упокой" ..
Вот ты вызвал recv(), прочитал N байт в указанный буфер. Ну и делай с ними все что душе угодно ! Хоть как строковые данные интерпретируй их, хоть как двоичные, хоть с asm, хоть без asm - winsock"у это абсолютно фиолетово, это уже прикладной уровень.


> можно применять массив char, и вручную копировать столько
> символов, сколько вернет recv


Да что хочешь, то и применяй)
К winsock это, повторяю, не имеет никакого отношения.
Буфер м.б. любого типа - хоть char, хоть byte, хоть что угодно...
Имея в буфере некие данные, ты волен трактовать их содержимое так как этого требует логика программы.


 
_Sergey_ ©   (2006-09-14 22:22) [78]

Все понятно. Как протестирую реализацию со SetThreadAffinityMask - напишу про результаты.


 
_Sergey_ ©   (2006-09-15 22:13) [79]

Использование SetThreadAffinityMask не помогло. Привязал всех клиентов на машинке с P4 3.0 GHz HT к 0-му процессору командой SetThreadAffinityMask(GetCurrentThread, 1). ОС - WinXP Pro SP2. При этом при включенном HT, запущенных 39 клиентах и 1-м сервере с периодичностью в 15 мин. клиенты "вываливаются" с MessageBox-ами с сообщениями об ошибках работы с памятью, причем с разными адресами. Примеры:
;-------
Инструкция по адресу "0x00140190" обратилась к памяти по адресу "0x98449844". Память не может быть "written".
"OK" -- завершение приложения
;-------
Инструкция по адресу "0x77f745cc" обратилась к памяти по адресу "0xffffffff". Память не может быть "read". "OK" -- завершение приложения
;-------
Инструкция по адресу "0x00140199" обратилась к памяти по адресу "0x00002930". Память не может быть "written".
"OK" -- завершение приложения
;-------
Инструкция по адресу "0x77f745cc" обратилась к памяти по адресу "0x00000007". Память не может быть "written".
"OK" -- завершение приложения
;-------
Инструкция по адресу "0x00000000" обратилась к памяти по адресу "0x00000000". Память не может быть "read". "OK" -- завершение приложения
;-------
Причем частота появления ошибок увеличивается при активном параллельном использовании "тяжелых" сторонних программ во время проведения теста (Photoshop, FineReader, ...), особенно при их запуске/завершении.
Дополнительная информация: В своих программах использую такие юниты: SysUtils, WinSock, Windows, Classes. Перезагружаю машинку, выключаю в BIOS-е HT, провожу тот же тест - никаких сообщений об ошибках. Все чисто. Что посоветуете, какие мысли по поводу вышеизложенного?


 
Eraser ©   (2006-09-15 23:00) [80]

> [79] _Sergey_ ©   (15.09.06 22:13)

ну что ж.. скорее всего ошибка в 17 строке, как говорится.. просто на процессоре с HT, вероятность её возникновения больше.


 
_Sergey_ ©   (2006-09-16 21:23) [81]

Надо полагать, что, наверное, ошибка вкралась или в библиотеке сокетов, или в компиляторе, или в стандартных юнитах. Ведь, на момент написания некоторых из этих продуктов HT еще не было, и адаптировать их под эту технологию никто не мог. Но что сделаешь, я так понял, исправить это нельзя. Спасибо за помощь. Буду пытаться сделать так, чтоб мою программу использовали. А HT придется отключать в BIOS на тех машинках, где эта технология присутствует. Будем надеяться, что на Intel Core 2 Duo и будущих 4-ядерных настольных процессорах будет все работать как надо. Ведь все-таки будут не виртуальные процессоры, как сейчас, а "реальные". Хотя, и их не скоро купят. Цены ведь еще кусаются.


 
Eraser ©   (2006-09-16 23:38) [82]

> [81] _Sergey_ ©   (16.09.06 21:23)
> Надо полагать, что, наверное, ошибка вкралась или в библиотеке
> сокетов, или в компиляторе, или в стандартных юнитах.

из своего и не только своего опыта скажу так - 95%, что это ошибка в вашем коде.


 
_Sergey_ ©   (2006-09-17 14:23) [83]

С последним заявлением я согласен. Статистика и вероятность не на моей стороне :) Но, как Вы можете прокомментировать такую ситуацию:
- при включенном HT тестирование приводит к вышеуказанным ошибкам;
- при выключенном HT программа может работать в условиях тестирования сутки, и ни одной ошибки не возникает.

В своей программе я не использую ф-ий работы с многопроцессорными системами. Более того, можно подумать, что я неправильно синхронизирую потоки, и такая синхронизация "проходит" на однопроцессорной системе, но не "проходит" на многопроцессорной. Но у меня и поток ведь один. Нечего там синхронизировать.

P.S> Личный опыт: у знакомого плагин браузера на основе DJVU Reader намертво зависал при открытии соотв. файла, причем каждый раз в разные моменты времени. Иногда удавалось просмотреть 2 странички документа, иногда 3-4. Причем одного и того же файла. У него: P4 3.0 HT, WinXP Pro SP1 Rus. При выключении HT проблема исчезла. А вот поставил он Win2k Pro SP4 Rus. И при включенном HT тот же плагин для просмотра *.djvu работал прекрасно. И что теперь думать? На что грешить? Просмотрщик, Win или HT?


 
Eraser ©   (2006-09-17 14:31) [84]

> [83] _Sergey_ ©   (17.09.06 14:23)

на сколько мне известно, Делфи 7 тоже глючила с включенным HT.
Тут нужно смотреть, какие подводные камни есть, при использовании этой технологии. В книге "Внутреннее устройтсво Виндовз" Солмона с Руссановичем эту информацию, при желании можно найти.

Так же задайте вопрос по HT в "WinAPI" или "Основной" конференции этого сайта, дело в том, что многие специалисты по внутреннему устройству ОС, в сетевую конференцию не читают :)


 
_Sergey_ ©   (2006-09-18 13:26) [85]

Спасибо за помощь. Попробую задать вопрос или найти информацию.



Страницы: 1 2 3 вся ветка

Форум: "Сети";
Текущий архив: 2007.02.25;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.88 MB
Время: 0.049 c
2-1170850723
smaller
2007-02-07 15:18
2007.02.25
Не ловится исключительная ситуация:


15-1168949697
Vlad Oshin
2007-01-16 15:14
2007.02.25
Рационализаторская идея про стир.машины


15-1170409028
мжмж
2007-02-02 12:37
2007.02.25
Может не сюда, но все же..


15-1170451847
votija
2007-02-03 00:30
2007.02.25
SQL файла и PHP


3-1164963356
zdm
2006-12-01 11:55
2007.02.25
DEL FROM DBF





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский