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

Вниз

Сокеты. Проверка программы клиента наличие программы сервера.   Найти похожие ветки 

 
Fredericco   (2003-02-18 10:56) [0]

Здравствуйте уважаемые мастера!
У меня есть определенная проблема, самостоятельно которую, я не в силах решить.
Вообщем ситуация такова. Есть много программ клиентов, которые без сервера выполняют некую работу. Однако при запуске сервера, не важно где на этой же машине или на удаленной, клиенты должны подключиться к запущенному только что серверу, для централизованного их управления в системе, а не по отдельности. Так вот, до сих пор на клиентской стороне я по таймеру, если нет соединения с сервером, пытаюсь его установить методом TClientSocket.Open. Обрабатываю событие OnError по ErrorCode определяю, что сервера нет и пробую заново. В инете прочел что при неудачной попытке установить соединение сокет не сразу высвобождает свои ресурсы и поэтому после длительных неудачных попыток вылетает ошибка Asynchronic Socket Error. Как один из выходов из этой ситуации - это установить большой интервал таймера, однако это не подходит: из-за специфики работы системы требуется быстрое подключение и интервал таймера более 2-х секунд не желателен.
Не подскажите, нет ли какого способа простой проверки на наличие открытого порта по заданному IP и номеру порта, при этом не расходуя ресурсов сокета?

Заранее спасибо!
С уважением Fredericco.


 
Digitman   (2003-02-18 11:17) [1]

procedure MyForm.ClientSocketError(...);
begin
Socket.Disconnect(Socket.Handle); // вот оно - освобождение ресурсов !
ErrorCode := 0;
... и т.д.

end;


 
Fredericco   (2003-02-18 11:57) [2]

Сделал все именно так.
Только:
. интервал таймера 1000;
. пытаюсь соединиться, выключаю таймер.
. OnError как ты и говоришь, включаю таймер.
. OnDisconnect включаю таймер.

На приблизительно тысячной попытке Asynchronic Socket Error 10055


 
Digitman   (2003-02-18 12:38) [3]


> Fredericco


код покажи ! Быть того не может, че-то ты там намудрил явно)


 
Fredericco   (2003-02-18 12:48) [4]


procedure TMySocket.csDrvError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
Socket.Disconnect(Socket.Handle);
Form1.Memo1.Lines.Add(IntToStr(ErrorCode));
ErrorCode:=0;
MyTimer.Enabled:=True;
SetLength(SendToSocket,0);
HaveCon:=False;
end;


procedure TMyTimer.IsTime(Sender: TObject);
begin
if (csDrv.Active=False) then begin
MyTimer.Enabled:=False;
ConnectToCM;
end;
end;


procedure ConnectToCM;
begin
try
if host<>"error" then begin
csDrv.Host:=Host;
csDrv.Open;

// csDrv.Socket.OnSocketEvent:=csDrv.csDrvOnSocketEvent;
end;
except
end;
end;




 
Digitman   (2003-02-18 13:46) [5]

предполагается, что изначально MyTimer.Enabled := False


var
InConnecting: Boolean = False;
...
procedure TMySocket.csDrvError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
if InConnecting then
begin
Socket.Disconnect(Socket.Handle);
MyTimer.Enabled:=True;
end;
Form1.Memo1.Lines.Add(IntToStr(ErrorCode));
ErrorCode:=0;
...
end;

procedure TMySocket.csDrvConnecting(Sender: TObject; Socket: TCustomWinSocket);
begin
IsConnecting := True;
end;

procedure TMySocket.csDrvConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
IsConnecting := False;
end;

procedure TMySocket.csDrvDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
MyTimer.Enabled:=True;
end;

procedure TMyTimer.IsTime(Sender: TObject);
begin
MyTimer.Enabled:=False;
ConnectToCM;
end;


procedure ConnectToCM;
begin
if host<>"error" then begin
csDrv.Host:=Host;
csDrv.Open;
end;
end;


 
Fredericco   (2003-02-19 21:43) [6]

Извини, что так долго не писал: у меня только сейчас сессия началась.
Я сделал все в точности как ты написал.
Неожиданностью для меня стало, что проходит пару секунд между попыткой соединения и возникновением события об ошибке.
Я прождал до 139 без успешных попыток, потом мне надоело ждать и я запустил еще 10-ть копий той же самой программы. Через 3-4 попытки соединения каждой программы (то есть всего 30-40 попыток соединения) вылезла пресловутая 10055.


 
Digitman   (2003-02-20 10:26) [7]

Нюансов здесь немало, и я вот тебе что посоветую.

Вместо "слепых" экспериментов вникни в логику и последовательность происходящего в ходе исполнения метода Open:

Этап 1. Попытка создания гнезда как сист.ресурса.

Внутри вызывается ф-ция winsock.socket(). Если перед этим (при ранних вызовах Open) было много успешных внутренних вызовов winsock.socket() без соответствующих внутр.вызовов winsock.closesocket(), то рано или поздно соотв.ресурсы будут исчерпаны, о чем ты будешь извещен исключением ESocketError (cannot create socket), при этом реальный код ошибки - WSAENOBUFS=10055. Это исключение "ловится" заключением вызова Open в try..except, обнаруживается сразу же (до OnError еще дело не доходит) и сигнализирует о том, что твои более ранние вызовы метода Open (не приведшие непосредственно к исключению в блоке try..except) приводили к возникновению события OnError(), в обработчике которого гнездо как сист.ресурс не было освобождено явным вызовом Disconnect();

Этап 2. Попытка обработки параметров коннекта.

Гнездо как выделенный ресурс уже существует.
Возбуждается событие OnLookup(), говорящее о том, что после его обработки будет произведен анализ параметров и попытка их разрешения :
- при непустом св-ве Host с пом. DNS производится попытка разрешения имени хоста в IP-адрес; успешная попытка устанавливает св-во Address, равное значению IP-адреса найденного хоста; неуспешная приводит к возбуждению события OnError(), в котором следует освободить гнездо-ресурс вызовом метода Disconnect();
- при пустом св-ве Host и пустом св-ве Address событие OnError() не возникнет, вместо этого метод Disconnect() будет вызван автоматически по исключению ESocketError (no address);
- при непустом св-ве Service с пом. лок.подсистемы регистрации имен сервисов производится попытка преобразования имени сервиса в номер порта; успешная попытка устанавливает зн-е св-ва Port; неуспешная приводит к возбуждению события OnError(), в котором следует освободить гнездо-ресурс вызовом метода Disconnect();

Этап 3.

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

Этап 4.

Производится попытка коннекта внутренним вызовом winsock.connect(). По результатам попытки возбуждается либо событие OnConnect() либо OnError():

- OnConnect() сообщает об успешном установлении соединения с сервером, т.е. хост доступен и акцептировал кл.запрос на соединение по указанному порту. В дальнейшем, при штатном (по инициативе любой из сторон соединения) дисконнекте клиентское гнездо-ресурс будет освобождено автоматически внутренним вызовом Disconnect(); при нештатном (разрыв коннект по иным причинам в момент приема/передачи) возникнет событие OnError, при обработке которого следует явно освободить гнездо-ресурс вызовом Disconnect()

- OnError() сообщает о неуспешной попытке соединения с сервером по различным (не связанным с кл.стороной) причинам: неверный IP-адрес, хост недоступен, порт неактивен, запрос на соединение отвержен сервером, превышение тайм-аута ожидания отклика сервера и т.д. и т.п. При обработке такого события так же следует явно освободить гнездо-ресурс вызовом метода Disconnect()

--------------------------------------------------
Вот теперь проанализируй все это (сопоставляя с кодом в scktcomp.pas), построй у себя в голове четкую и стройную схему (что, как и при каких обстоятельствах происходит на этой "кухне") и прими самостоятельно алгоритм.решение, в каких случаях во избежании "граблей" с WSAENOBUFS=10055 следует явно вызывать Disconnect(), а в каких это будет сделано изнутри компонента, т.е. автоматически.


 
Fredericco   (2003-02-20 11:48) [8]

Спасибо!
Буду размышлять.


 
blast   (2003-02-20 12:41) [9]

Ребята :) Это все конечно ХА-РА-ШО, но проблема раскрыта не полностью:

В Борландовом компоненте TClientSocket есть маааленькая ошибочка - забыли закрыть сокет при ошибке и в итоге через опр. время если клиент постоянно пытается законнектиться к серверу а его нет, происходит переполнение системного буфера сокетов... Я сталкивался с этой проблемой еще на Win98 там под буфер отведено 254 элемента. Мой клиент стучался с интервалом 1 секунда и через ~15 минут (самое длительное время из всех попыток) появлялось окно с ошибкой 10055 (WSAENOBUFS или No buffer space available), хотя ВСЕ проверки и были СДЕЛАНЫ и ОБИШКИ ОТКЛЮЧЕНЫ !!! По мноему мнению сообщение посылается от Операционки - поэтому не влияет тот факт, что ErrorCode := 0.

Выход: Воспользоваться другими компонентами или при ошибке ПРОСТО закрыть сокет АПИшной функцией. Дескриптор или SocketHandle в компоненте доступен.

Закрывать сокет нужно именно АПИшной ф-цией, т.к. в .Close() - ОШИБКА !!

to Digitman:
ЭТО:
Socket.Disconnect(Socket.Handle);
НЕ БУДЕТ РАБОТАТЬ Т.К. ОШИБКА В КОМПОНЕНТЕ!!!

Немного лирики: Я месяца 3 - 4 потратил на выяснение этого. Никто не отвечал - даже про ошибку не знали. Перепробовал МНОГО всего и компоненты и ДАЖЕ на нижнем уровне сокеты соединял. Потом все-т-ки нашелся ДОБРЫЙ человек, который разъяснил в чем дело и все заработало как надо.

Мое мнение - удобнее TClientSocket - TServerSocket.

Все! Всем успехов и удачи


 
Digitman   (2003-02-20 12:54) [10]


> НЕ БУДЕТ РАБОТАТЬ Т.К. ОШИБКА В КОМПОНЕНТЕ!!!


где аргументы ?


 
Digitman   (2003-02-20 13:12) [11]


> blast


Это не ошибка в компоненте. Борланд намеренно не выполняет Disconnect() после завершения обработки OnError(). Мало ли какие отказы могут возникнуть ! В рябе случаев незакрытое гнездо может еще понадобиться, поетому выполнять closesocket() безусловно при любом отказе было бы неразумным


 
blast   (2003-02-20 13:19) [12]

Не при любом, а при:
- отсутствии хоста
- незапущенной серверной части
- перезапуске хоста
- shutdown"е хоста
- обрыве соединения

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

Вот такие вот дела
Blast ;)


 
Digitman   (2003-02-20 13:44) [13]

?

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


 
dimich1978   (2003-02-20 13:47) [14]

И что же с этим делать тогда:
Socket.Disconnect(Socket.Handle);
>>Закрывать сокет нужно именно АПИшной ф-цией, т.к. в .Close() - >>ОШИБКА !!

Какой?


 
blast   (2003-02-20 13:49) [15]

Да зачем рыть вглубь ?? Ему нужно чтобы при отсутствии сервера не возникало ошибки 10055!! Так проще по опр. ошибкам (см. выше) просто делать CloseSocket и всё...


 
Digitman   (2003-02-20 14:03) [16]


> blast © (20.02.03 13:49)


вот чтобы не было ошибки 10055, нужно всего лишь при определенном коде ErrorCode выполнить Socket.Disconnect() ... или Socket.Close ... и не говори ерунды, что это не работает !
Просто тот , у кого этот отказ возникает, не учитывает все возможные причины отказа лукапа/коннекта



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

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

Наверх




Память: 0.51 MB
Время: 0.008 c
14-44369
TIER
2003-03-22 08:17
2003.04.14
Как программно скопировать файл по локалке ???


6-44313
_sMile
2003-02-22 21:24
2003.04.14
Как передать Bitmap из TImage, используя сокеты?


6-44316
Adventure
2003-02-23 16:06
2003.04.14
Совсем смешной вопрос!


1-44252
asdfasdf
2003-03-31 20:14
2003.04.14
Факториал


14-44353
Карлосон
2003-03-30 23:17
2003.04.14
Hello Programist А можно ли перевести Дату в числовой тип данных





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский