 |  | |  | |
Использование сокетов в Delphi. Часть вторая: сокеты Windows | Полный текст материала
Другие публикации автора: Антон Григорьев
Цитата или краткий комментарий: «... В предыдущей статье цикла «Использование сокетов в Delphi» мы рассмотрели те методы работы с сокетами, которые восходят ещё к сокетам Беркли. Разработчики библиотеки сокетов для Windows добавили в неё также поддержку новых методов, упрощающих работу с сокетами для приложений, имеющих традиционную для Windows событийно-ориентированную модель. В Windows можно использовать асинхронные сокеты и перекрытый ввод-вывод. ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 18 | 100% | | | | Ничего особенно нового и интересного | [2] | 0 | 0% | | | | Написано неверно (обязательно укажите почему) | [3] | 0 | 0% | | Всего проголосовали: 18 | | | Все понятно, материал читается легко | [1] | 11 | 91.7% | | | | Есть неясности в изложении | [2] | 1 | 8.3% | | | | Непонятно написано, трудно читается | [3] | 0 | 0% | | Всего проголосовали: 12 |
[UDP] [WinSocket]
Отслеживать это обсуждение 
Всего сообщений: 3912-02-2012 05:53Спасибо Антону еще раз за эти статьи. Обнаружил интересное обстоятельство. Разбирался с использованием событий сокетов, обнаружил, что если мы создаем событие, "подключаем" его к событию подключения сокета, и вызываем connect (безо всяких своих дополнительных потоков (нитей)), то при этом у процесса появляется еще один поток. Даже если закрыть сокет, поток остается, если вызвать WSACleanup(), то он исчезает. Видимо библиотека сокетов Ws2_32.dll сама создает этот поток.
Код: создали сокет, задали адрес и порт, потом
ConnectEvent := WSACreateEvent();
WSAEventSelect(ClientSocket, ConnectEvent, FD_CONNECT);
Result := connect(ClientSocket, Addr, SizeOf(Addr));
Если событие даже и создать, но не использовать (незачем его создавать конечно в таком случае), то поток не возникает
ConnectEvent := WSACreateEvent();
Result := connect(ClientSocket, Addr, SizeOf(Addr));
|
|
08-02-2011 09:11По материалам данной статьи я сделал специфический сервис для чтения SNMP пакетов. SNMP пакеты с точки зрения системы являются простыми UDP пакетами. Специфика заключалась в отправке и приеме 70000 пакетов. Причем необходимо было сделать скользящее окно одновременной отправки не более 100 пакетов. И тут я увидел информацию по перекрытому вводу-выводу с использованием процедур завершения. На этой основе я и построил свою программу. Что хотелось бы отметить:
1) Несмотря на то что процедура завершения выполняется в том же потоке, что вызвавший ее поток, необходимо защищать переменные.
2) Процедура SleepEx(n, True) выполняется без задержки!
У меня пакеты могут не придти или придти с опозданием.
|
|
13-09-2010 03:51Извиняюсь, недогляд, FD_CONNECT там не придет, т.к. не было Select-а для этого события.
Общности ради хотелось бы уточнить верно ли это утверждение:
Для слушающего сокета могут прийти только события FD_ACCEPT. Для подключенного сокета все события кроме FD_ACCEPT. |
|
13-09-2010 03:48Проверка наверное все таки нужна я так полагаю. Если мы на слушающем сокете при входящем подключении сделаем accept, то созданный сокет пронаследует все "настройки" слушающего, и будет асинхронным и все события будут посылаться в это же окно с тем же сообщением. Следовательно в оконную функцию придет FD_CONNECT который вполне может завершиться неудачей, и нужно будет закрыть сокет. |
|
13-09-2010 02:31сообщение от автора материала Shoor:
Сейчас, если честно, не могу вспонить, зачем я написал эту проверку. Никакого вызова сокетной функции, которая могла бы установить такую ошибку, перед этим нет, сокет передаётся уже готовым и заносится в переменную простым приведением типа. Подозреваю, что эту проверку я вставил на автомате, а на самом деле она тут не нужна вообще. По крайней мере, в справке по WSAAsyncSelect я не заметил указаний на необходимость такой проверки. Более того, по логике вещей WSAGetLastError не сможет здесь вернуть правильную ошибку - мало ли какие сокетные функции вызывались в промежутке между постановкой сообщения в очередь и его обработкой. Наверное, лучше всего выкинуть эту проверку вообще. |
|
12-09-2010 15:25хотелось бы уточнить относительно вот этого кода в статье:
procedure TForm1.WMSocketEvent(var Msg:TMessage);
var Sock:TSocket;
SockError:Integer;
begin
Sock:=TSocket(Msg.WParam);
SockError:=WSAGetSelectError(Msg.lParam);
if SockError<>0 then
begin
CloseSocket(Sock);
Exit
end;
case WSAGetSelectEvent(Msg.lParam) of
FD_Read:
begin
end;
FD_Accept:
begin
Accept(Sock,nil,nil)
end;
FD_Close:
begin
Shutdown(Sock,SD_Send);
CloseSocket(Sock)
end
end
end;
мы сразу закрываем сокет:
if SockError<>0 then
begin
CloseSocket(Sock);
Exit
end;
хотя поидее в значении Sock может быть и слушающий сокет. Не знаю возникают ли ошибки при FD_ACCEPT но мне кажется неправильно вот так вот закрывать сокет, сначала надо бы отфильтровать FD_ACCEPT. Если не прав - поправьте. |
|
18-05-2010 09:47Антон, огромное спасибо статья содержит весьма практические сведения !
менее чем за час удалось разобраться с неблокируемым режимом!
семью строками кода удалось запустить клиент !
с WSAStringToAddress, наверно и того меньше будет :)
WSAStartup($101, Data);
ClientSock := Socket(AF_Inet, Sock_Stream, 0);
addr.sin_family := AF_INET;
addr.sin_addr.s_addr := inet_addr(PAnsiChar('94.100.177.6'));
addr.sin_port := htons(110);
connect(ClientSock, addr, sizeof(sockaddr_in));
WSAAsyncSelect(ClientSock, Handle, WM_SocketEvent, FD_Read or FD_Accept or FD_Close);
также не составило особого труда разобраться и запустить обработчик из вашего примера!
после вашей статьи сложилось впечатление, что документация из официальных источников специально вводит людей в заблуждения чтоб они никогда не смогли сами разобраться с теперь уже очевидными вещами, подтверждению этому куча ненужных примеров идущих в комплекте с дельфи, и как вы сами заметили отсутствие важных прототипов функций таких как WSAStringToAddress и.др...
|
|
02-02-2010 13:28сообщение от автора материала Какие еще есть более производительные способы.
Они описаны во второй части статьи. Правда, там не описаны порты завершения ввода-вывода, которые являются самым производительным, ориентированным на массовое обслуживание клиентов средством, но после приведённой там информации вы с этим разберётесь самостоятельно. |
|
02-02-2010 13:18В этом абзаце:
"Преимущество такого сервера по сравнению с сервером, основанным на функции Select (пример кода такого сервера приведён в предыдущей статье), заключается в том, что при отсутствии событий на сокетах он не находится в приостановленном режиме и может выполнять какие-то другие действия (например, реагировать на действия пользователя). Кроме того, этот сервер не имеет ограничений по количеству подключаемых клиентов, связанных с размером типа TFDSet. Впрочем, последнее не является существенным преимуществом, так как при таком количестве клиентов сервер обычно использует другие, более производительные способы взаимодействия с клиентами."
Хотелось бы узнать подробности по последнему предложению. Какие еще есть более производительные способы. А так же интерсно бы услышать какие методы
наиболее удачны по отражению DoS-атак. Хотя на мой взгляд имется в этом вопросе принципиальная проблема. С одной стороны сервер должен максимально быстро обслуживать клиентов, а это как раз "улучшает" DoS-атаку :) |
|
14-12-2009 13:08сообщение от автора материала Hunter Black:
За неимением сети попробовал локально - всё работает. На форме лежит кнопка с вот таким обработчиком:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
S:TSocket;
Addr:TSockAddr;
begin
TAcceptThread.Create(False);
S:=Socket(AF_Inet,Sock_Stream,0);
Addr.sin_family:=PF_Inet;
Addr.sin_addr.S_addr:=Inet_Addr('127.0.0.1');
Addr.sin_port:=HtoNS(3320);
FillChar(Addr.sin_zero,SizeOf(Addr.sin_zero),0);
Connect(S,Addr,SizeOf(Addr));
Sleep(1000);
CloseSocket(S)
end;
Нить TAcceptThread описана так:
type
TAcceptThread = class(TThread)
private
FS:array of Byte;
St:string;
protected
procedure ShowFS;
procedure Execute; override;
end;
...
function TestAccept(
lpCallerId,lpCallerData:PWSABuf;
lpSQOS,lpGQOS:Pointer;
lpCalleeID,lpCalleeData:PWSABuf;
g:Pointer;
dwCallbackData:DWORD):Integer;stdcall;
var
Thread:TAcceptThread;
begin
Thread:=TAcceptThread(dwCallbackData);
SetLength(Thread.FS,lpCallerId.Len);
Move(lpCallerId.Buf^,Thread.FS[0],lpCallerId.Len);
Result:=1
end;
procedure TAcceptThread.Execute;
var
S:TSocket;
Addr:TSockAddr;
Sz:Integer;
I:Integer;
begin
S:=Socket(AF_Inet,Sock_Stream,0);
Addr.sin_family:=PF_Inet;
Addr.sin_addr.S_addr:=InAddr_Any;
Addr.sin_port:=HtoNS(3320);
FillChar(Addr.sin_zero,SizeOf(Addr.sin_zero),0);
Bind(S,Addr,SizeOf(Addr));
Listen(S,SoMaxConn);
Sz:=SizeOf(Addr);
WSAAccept(S,@Addr,@Sz,TestAccept,DWORD(Self));
CloseSocket(S);
for I:=0 to High(FS) do
St:=St+IntToStr(FS[I])+' ';
Synchronize(ShowFS)
end;
procedure TAcceptThread.ShowFS;
begin
ShowMessage(St)
end;
Цифры 127.0.0.1 в возвращаемом результате хорошо видно. |
|
14-12-2009 04:39При использовании функции WSAAccept и параметра LPCONDITIONPROC, возникает следующая проблема. Параметры callback-функции lpCallerId и lpCalleeId всегда и с любого компа возвращают IP-адреса "0.0.0.0". В чем может быть проблема? |
|
25-05-2008 17:25хорошие статьи. ждем продолжения :) |
|
18-05-2008 03:12Надеюсь, про третью часть это не шутка была, и она вот-вот появится?.. ;-) |
|
28-06-2007 03:08Антон , а можно примерчики, выложит где-нибудь. |
|
28-06-2007 03:05
13-06-2007 22:14Эх, когда же наступит 1 декабря 2009 года...Ну хотя бы 30 сентября 2008.... |
|
23-03-2007 07:37Отличные статьи, большое спасибо автору |
|
21-03-2007 15:54Огромное спасибо за инфо. |
|
20-02-2007 11:45Событие ведь не сообщения - количество SetEvent узнать невозможно, поэтому в MSDN и пишут про атомарность WSAEnumNetworkEvents
Спасибо, теперь понятно. |
|
20-02-2007 07:18второй параметр этой фнкции можно же и не заполнять, сбросив событие вручную после обработки всех сокетов.
Ну так вы его сбросите, а оно может во время обработки WSAEnumNetworkEvents кем-то другим выставится. Событие ведь не сообщения - количество SetEvent узнать невозможно, поэтому в MSDN и пишут про атомарность WSAEnumNetworkEvents, иначе второй параметр был бы вообще не нужен, сами бы сбросили через ResetEvent. |
|
19-02-2007 12:57Только один, так как иначе не обеспечить нормальную работу WaitFor* функции и WSAEnumNetworkEvents - будет потеря некоторых событий.
Почему? Данные для WSAEnumNetworkEvents ведь вроде берутся из сокета, а не из события; второй параметр этой фнкции можно же и не заполнять, сбросив событие вручную после обработки всех сокетов. |
|
19-02-2007 12:38И ещё, сколько сокетов могут быть связаны с одним событием, только один или несколько?
Только один, так как иначе не обеспечить нормальную работу WaitFor* функции и WSAEnumNetworkEvents - будет потеря некоторых событий. Вам придётся проходить по всем сокетам ипользующим этот Event и вызывать WSAEnumNetworkEvents, так вот в промежутке между вызовами Event может выставится другим сокетом и вы потеряете какое нибудь событие. |
|
19-02-2007 12:02Спасибо за стати.
Есть некоторые неясности в работе асинхронных скоетов, которые, думаю, следует уточнить в статье:
Если необходимо использовать асинхронный сокет в блокирующем режиме, после вызова WSAAsyncSelect требуется перевести его в этот режим вручную.
Вот в MSDN пишут:
The WSAAsyncSelect function automatically sets socket s to nonblocking mode, regardless of the value of lEvent. To set socket s back to blocking mode, it is first necessary to clear the event record associated with socket s via a call to WSAAsyncSelect with lEvent set to zero. You can then call ioctlsocket or WSAIoctl to set the socket back to blocking mode. For more information about how to set the nonblocking socket back to blocking mode, see the ioctlsocket and WSAIoctl functions.
The WSAEventSelect function automatically sets socket s to nonblocking mode, regardless of the value of lNetworkEvents. To set socket s back to blocking mode, it is first necessary to clear the event record associated with socket s via a call to WSAEventSelect with lNetworkEvents set to zero and the hEventObject parameter set to NULL. You can then call ioctlsocket or WSAIoctl to set the socket back to blocking mode.
Так вот вопрос: таки можно ли работать в асинхронном режиме с блокирующим сокетом или нет?
Если да, то думаю в статье стоит указать что это несмотря на противоречие с MSDN.
Блокирующий асинхронный режим, думаю, можно было бы применить так: accept и recv можно вызывать по FD_xx событиям, а send пусть блокирует, тогда мы, как в примерах, не учитываем FD_WRITE, но уверены что данные не потеряютя при переполнении буфера.
И ещё, сколько сокетов могут быть связаны с одним событием, только один или несколько ? В MSDN информации на этот счёт не нашёл. |
|
14-01-2007 00:44сообщение от автора материала Узкое место передачи данных через сеть - это именно сама передача, а не то, как организована работа с буфером в памяти, в который эти данные поступили. Так что используйте такие способы работы с сокетами, которые вам удобнее. |
|
13-01-2007 05:13Есть ли какая-нибудь информация о производительности Windows Sockets vs Berkley Sockets? Какие сокеты лучше использовать при написании высоко-производительных приложенй? Скажем, есть необходимость очень быстро обойти много сайтов и что-нибудь скачать. Какие вообще есть рекомендации по повышению производительности в таком случае? И есть ли смысл использовать асинхронные сокеты или лучше использовать много потоков, в каждом - блокирующий сокет. |
|
12-12-2006 04:06Спасибо Антону за подробную статью.
Я тоже с нетерпением жду третьей части :), а примери будут? |
|
01-12-2006 06:22сообщение от автора материала Но нельзя ли в дополнение к ней представить образцы полнофункциональных клиента и сервера на Winsock2? Это бы очень помогло для обучения "методом научного тыка" ;)
Это готовится. Уже пять примеров готово (правда, комментарии в коде пока не расставлены), постепенно готовлю остальные. |
|
01-12-2006 06:14Спасибо за содержательные публикации, Антон
Но нельзя ли в дополнение к ней представить образцы полнофункциональных клиента и сервера на Winsock2? Это бы очень помогло для обучения "методом научного тыка" ;) |
|
29-11-2006 06:47
07-11-2006 04:48Если программа выполняет одновременно несколько операций перекрытого ввода-вывода, встаёт вопрос, как при вызове процедуры завершения определить, какая из них завершилась. Для каждой такой операции должен быть создан уникальный экземпляр структуры TWSAOverlapped. Процедура завершения получает указатель на тот экземпляр, который использовался для начала завершившейся операции. Можно сравнить указатель с теми, которые использовались для запуска операций перекрытого ввода-вывода, и определить, какая из них завершилась. Это не всегда бывает удобно из-за необходимости где-то хранить список указателей, использовавшихся для операций перекрытого ввода-вывода. Существуют ещё два варианта решения этой проблемы. Первый вариант заключается в создании своей процедуры завершения для каждой из выполняющихся параллельно операций. Этот вариант приводит к получению громоздкого кода и может быть неудобен, если число одновременно выполняющихся операций заранее неизвестно. Его следует использовать только при одновременном выполнении разнородных опреаций, требующих разных алгоритмов при обработке их завершения. Другой вариант предлагается в MSDN'e. Так как при использовании процедур завершения значение поля hEvent структуры TWSAOverlapped игнорируется системой, программа может записать туда любое 32-битное значение и с его помощью определить, какая из операций завершена. В строго типизированном языке, каким является Паскаль, подобное смешение типа дескриптора и целого выглядит весьма непривлекательно, но, к сожалению, это лучшее из того, что нам предлагают разработчики WinSock API.
Встречал еще один вариант:
Вместо структуры TWSAOverlapped использовать свою TWSAOverlappedEx, расширенную необходимыми параметрами, только при передаче указателя на нее, приводить к типу PWSAOverlapped. Также в Паскале смотриться некрасиво... |
|
11-10-2006 04:58Ну и когда же 3-я часть бум? |
|
09-12-2005 10:14Хорошо написано)
з.ы. А полгода-то прошло)Сообщение не подписано |
|
08-12-2005 13:24Очень хорошо, но хотелось полнее. Уж не просто самому разобраться. Без этой статьи у меня бы совсем ничего не получилось... Пытаюсь написать майлер, чтобы отправлял письма по SMTP. Пишу на SP-FORT'е, это очень интересно. Сообщение не подписано |
|
23-11-2005 11:22порядочное кол-во описок и недочетов.
детально расписывать не буду, но например:
"Параметр lpCalleeId содержит адрес интерфейса, принявшего соединение (поля структуры при этом используются так же, как у параметра lpCallerId). В предыдущей статье обсуждалось, что сокет, привязанный к адресу InAddr_Any, прослушивает все сетевые интерфейсы, имеющиеся на компьютере, но каждое подключение, созданное с его помощью, использует конкретный интерфейс. Параметр lpCalleeId содержит адрес, привязанный к конкретному соединению. Параметр lpCalleeData указывает на буфер, в который сервер может положить данные для отправки клиенту. Этот параметр также не имеет смысла при использовании протокола TCP, не поддерживающего отправку данных при соединении."
не работает это. всегда будет тот-же адрес что и при bind(), т.е. если был указан inaddr_any то и интерфейс будет inaddr_any. Увы. Проверялось на NT4 и Win2K.
|
|
01-09-2005 11:08
13-07-2005 07:39ждем третью часть. спасибо. Сообщение не подписано |
|
30-03-2005 07:48сообщение от автора материала Шансы есть, но не раньше, чем через полгода - сейчас мне пока не до этого. |
|
30-03-2005 07:21Антон!
У нас есть шансы все-таки увидеть третью часть или как? :)
С уважением,
Василий. |
|
21-10-2004 16:34Спасибо Антону за подробную статью.
С нетерпением жду третьей части :) |
|
|
|