![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Логика работы сокетов [D7, Win95/98, WinME, NT4, Win2k, WinXP]
_Sergey_ © (2006-09-05 22:57:00)Здравствуйте.
Недавно занялся сетевым программированием с использованием библиотеки 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 15:46:00) [81]
> не застраховано от дублирования пакетов
Что за ерунда ?
Как это связано с транспортным уровнем ?
Сергей М. (2006-09-06 15:40:00) [82]
> _Sergey_ © (06.09.06 15:27) [3]
> при оч. интенсивной нагрузке на сервер и большом кол-ве
> клиентов многие клиенты "зависали"
Ты о чем ? О "зависании" тех самых "многих" на вызове ими ф-ции connect() ?
_Sergey_ (2006-09-06 15:27:00) [83]Спасибо за ответ. Задача стоит таким образом, чтобы мое приложение (и сервер, и клиенты) работали, по крайней мере, начиная с Win98. Путем многих проб и экспериментов я реализовал логику, которая основывалась на втором посте. При проведении экспериментов с сокетами (W2k SP4)обнаружил, что параметр backlog queue size не оказывает никакого влияния на указанную ситуацию. При подключенном в данный момент 1-м клиенте 2-й подключался и при значении указанного выше параметра 1, 0, в данный момент этот параметр имеет значение SoMaxConn.
1-ый указанный Вами вариант реализовавал. Действительно, сервер не допускал подключения других клиентов, если хотя бы один был подключен (CF_REJECT). Но при оч. интенсивной нагрузке на сервер и большом кол-ве клиентов многие клиенты "зависали" (т.е., как я понял, не получали возврата из ф-ий библиотеки сокетов). Поэтому был реализован 2-й вариант. И как программист, который хочет понимать все в своей программе, возникли вопросы относительно логики работы сокетов. Соответствено, 1-ый и 2-ой вопросы в моем первом посте. Ведь протокол TCP/IP предотвращает дублирование и потерю пакетов, восстанавливает первоначальную очередность следования пакетов, предотвращает повреждение информации при передаче по линиям связи. Но, получается, то, что реализуется на нижнем уровне, то, о чем позаботились разработчики на нижнем уровне реализации, с тем приходится бороться на прикладном уровне, потому как мое приложение после введения обратной связи застраховано от потери пакета данных (пакет данных обязательно дойдет к серверу), но не застраховано от дублирования пакетов (маловероятная ситуация, но возможная).
Сергей М. (2006-09-06 08:40:00) [84]Впрочем можно "извратиться" и следующим образом:
При вызове listen() указать 2-м параметром (backlog queue size) значение = 1.
После успешного возврата из accept() тут жезакрыть слушающее гнездо и создавать его вновь лишь при разрыве связи с клиентом.
100%-й гарантии того, что ни один клиент более не подключится, нет и в этом случае, но на практике этого достаточно для реализации сабжа.
Сергей М. (2006-09-06 08:26:00) [85]
> задача: написать сервер, который допускал бы подключение
> только одного клиента
Для Win9x/Me/NT задача не решаема.
Сабж реализуем только для линейки ОС, начиная с W2k, где в отличии от предшествующих ОС функционирует опция гнезда SO_CONDITIONAL_ACCEPT (см. Get/SetSockOpt). Только включив эту опцию и используя WSAAccept() вместо accept() можно решить задачу так как она поставлена.