Konuyu Oyla:
  • Derecelendirme: 5/5 - 1 oy
  • 1
  • 2
  • 3
  • 4
  • 5
TIdTCPServer mikroişlemci koptuğunda onDisconnect tetiklenmemesi
#1
Merhaba, 

TIdTCPServer componentine iki adet Arduino Mega 2560 geliştirme kartını W5500 ethernet modülüyle(wiznet) ve bir adet ESP 32 kablosuz olarak Delphi VCL uygulamasına TCP portu üzerinden bağlıyorum.

TIdTCPServer onDisconnect olayında aşağıdaki kod bulunuyor:

var
LHost: string;
LItem: TListItem;
begin
TIdNotify.NotifyMethod(clientkoptu);
  TThread.Queue(nil,
procedure
begin
   LItem := Form1.ListView1.FindData(0, AContext, true, false);
   if LItem <> nil then LItem.Delete;
 //  ShowMessage('Bağlantı yok');
end);
   Form1.Memo1.Lines.Add(DateTimeToStr( Now ) +'  '+ 'Arduino Bağlantısı Koptu');
  //Arduino bağlantısının koptuğunu bildirdik
end;

Bu kod mikroişlemciler TCPServer'dan koptuğunda tetiklenmiyor çalışmıyor.
İnternet aramasına :

Alıntı:delphi indy detect onDisconnect
 
yazdığımda çıkan Remy Lebeau stackoverflow cevabında 
Alıntı: you can call the AContext.Binding.SetKeepAliveValues()
 method to enable keep-alives. The OS will then handle the keep-alives for you, and will invalidate the connection if the timeout elapses.

.SetKeepAliveValues kullanılmasını önermiş. Bende onConnect olayına 


 AContext.Binding.SetKeepAliveValues(True,500,500);


bu kodu ekledim. Bu kod çoğunlukla onDisconnect'in tetiklenmesini sağlıyor zaman zamanda sağlamıyor.
Aynı VCL uygulamasına FMX Android'den bağlanıp koptuğumda onDisconnect hemen tetikleniyor. 

SetKeepAliveValues haricinde kullanabileceğim bir yöntem var mı?
Bu yöntem %80 onDisconnect'i tetikliyor.

Değerli tavsiyelerinizi okumak isterim, iyi çalışmalar.
Öğrenci, EEM, SAÜ.
Youtube
Cevapla
#2
Önerim şu şekilde olurdu:

Protokolünüzde veri transferi sonunu ifade eden bir karakter vardır mutlaka. Ben tilda (~) kullanıyorum.

Veri akışı sona erdi demek için veriyi gönderen taraftan geliyor. Buraya kadar sadece diyalog üzerine konuştum.

Sunucu tarafta timer ile oluşacak yapı ile paket alış verişi olmadığı zamanlarda, periyodik olarak tüm clientlar ping~ yollamak ile yükümlü tutuyorum. Sunucu da cevap olarak pong~ yolluyor. 

Eğer iki veya üç ping gecikirse bağlantı koptu varsayarak soketi sunucu tarafından kapatıyorum, client disconnect event alır ya da almaz önemsemiyorum. Biliyorum ki client bir connect işlemi için çabalayacaktır.

Burada sorunuzun kısmen cevabı oluşuyor, ondisconnect kısmını timer içinden bu ping gecikmesi den sonra manuel olarak basit procedure çağırır gibş tetikleyebilirsiniz. 

Bu gecikme aynı zamanda sunucu tarafını süprizlerden de koruyor. 

Dilerseniz sunucu taraftan da periyodik olarak bağlı socketlere ters yönden ping~ da yollayabilirsiniz ama tavsiye etmem. Sunucu her daim sağlıklı olmalı, olası client defektlerinden etkilenmemelidir. 

ping pong alış verişi sunucu veya client bağlantı timeout olma ihtimaline karşı tazeleme işi de görüyor. 
Örneğin network kartı güç koruma moduna geçmiyor.

Başarılar.
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif
Cevapla
#3
(25-02-2021, Saat: 23:03)mrmarman Adlı Kullanıcıdan Alıntı: Önerim şu şekilde olurdu:

Protokolünüzde veri transferi sonunu ifade eden bir karakter vardır mutlaka. Ben tilda (~) kullanıyorum.

Veri akışı sona erdi demek için veriyi gönderen taraftan geliyor. Buraya kadar sadece diyalog üzerine konuştum.

Sunucu tarafta timer ile oluşacak yapı ile paket alış verişi olmadığı zamanlarda, periyodik olarak tüm clientlar ping~ yollamak ile yükümlü tutuyorum. Sunucu da cevap olarak pong~ yolluyor. 

Eğer iki veya üç ping gecikirse bağlantı koptu varsayarak soketi sunucu tarafından kapatıyorum, client disconnect event alır ya da almaz önemsemiyorum. Biliyorum ki client bir connect işlemi için çabalayacaktır.

Burada sorunuzun kısmen cevabı oluşuyor, ondisconnect kısmını timer içinden bu ping gecikmesi den sonra manuel olarak basit procedure çağırır gibş tetikleyebilirsiniz. 

Bu gecikme aynı zamanda sunucu tarafını süprizlerden de koruyor. 

Dilerseniz sunucu taraftan da periyodik olarak bağlı socketlere ters yönden ping~ da yollayabilirsiniz ama tavsiye etmem. Sunucu her daim sağlıklı olmalı, olası client defektlerinden etkilenmemelidir. 

ping pong alış verişi sunucu veya client bağlantı timeout olma ihtimaline karşı tazeleme işi de görüyor. 
Örneğin network kartı güç koruma moduna geçmiyor.

Başarılar.

Merhaba,

Her soruma zaman ayırıp cevap verdiğiniz için teşekkür ederim. Evde internet arızası olduğundan şimdi yazabiliyorum. 
Anlattığınız şekilde bir kurgu yapamadım, yapamam. Indy TCP işlemlerini forumdaki @3ddark örneği başta olmak üzere verilen cevaplara bakarak yazdım. 

(23-04-2020, Saat: 17:22)3ddark Adlı Kullanıcıdan Alıntı: Evet sorunun çözümünü Remy(rlebeau) nin dediği gibi yaptım ve Client sayısı arttı. 5000 Client ile kendi bilgisayarımda test ettim ve sorunsuz olarak bağlantıları gerçekleştirdim.
Hatta bir adım daha ileri gidip 10000 Client test ettim 5800+ kilitlendi.

Delphi 10.3 ile test ettim. Windows 10 x64 ve 16GB Ram

Sorunun çözümü şu şekilde oldu. Default Stack Size değerlerindeki Max Stack size değerini düşürdüm. Varsayılan Stack Size değerleri burada yazıyor.
Max Stack Size değerini "65536" olarak değiştirdim.

Bu Max Stack Size değerini değiştirime işlemi proje ayarı yerine Compiler Directive ile yapabiliriz.
{$MaxStackSize 65536}

Fakat ben bulamadım bu Stack Size değerinin birimi nedir? Bilen arkadaşlar yardımcı olursa sevinirim. 65536 Byte mı demiş olduk.

@SimaWB, @mcansız muhtemelen Lazarus tarafında çalışmasının nedeni de Stack Size değerlerinin varsayılan olarak Delphi ve Lazarus da farklı olması.

Lazarus tarafında default Stack Size değerini göremedim çok fazlada araştırmadım.


Test etmek isteyenler için proje kaynak kodlarının tamamı ekliyorum.

Fakat pes etmedim, boşta durmadım. Mikro işlemci kodları üzerinden tekrar geçtim. Delay komutunu kullanmaktan kaçındım onun yerine bir timer atadım. Arduino tarafında delay komutu işlemciyi blokladığı için istenmeyen, beklenmeyen hatalara sebep olduğu söyleniyor.
Bu geliştirmeyle birlikte VCL uygulaması onDisconnect olayını nerdeyse her seferinde yakalıyor. 

Anlamakta zorlandığım SetKeepAliveValues dahi kullanmadan FMX Android uygulaması onDisconnect olayını sorunsuz yakalayabiliyorken mikro işlemcilerde onDisconnect olayını neden yakalamakta zorlanıyor? onConnect ve onExecute olaylarında sorun olmazken sadece onDisconnect'te bu hatayla karşılaşılması ilginç bir durum.

Bu konudan bağımsız olarak bir süre önce TIdTCPServer'dan seçili client'a veri gönderme [çözüldü] başlığı altında sorduğum soruya verdiğiniz cevapta:

(22-11-2020, Saat: 19:37)mrmarman Adlı Kullanıcıdan Alıntı: Merhaba, adaptasyon için tebrikler.

- Proje geliştirirken yerel ağ ile sınırlı kalmamak için PORT'u da dahil ediyorum. 

Not: Verdiğim kodda siz IP verseniz de, listenizden ilgili IP'nin portunu ListView içindeki SubItems(0)'dan tespit ediyor olacağından yine çalışmasını beklerim.

- IoT teknolojileri LAN'dan WAN üzerine çıktığında, aynı evdeki örneğin (5) adet cihaz aynı LAN altında (5) farklı IP adresi alabiliyor iken, WAN üzerinden harici bir sunucuya ulaşırken sadece modemin yegane tek dış IP'si ile bağlanacaktır. Bu durumda IP yetmez PORT'u da dahil etmek lazım gelir. Çünkü aynı NAT altında çalışır. Bu durumda da ek bir işlem olarak bu portların da dahili ağda ilgili IP'lere modem router üzerinden yönlendirilmesi de gerekecektir ki şu aşamada sizin buna ihtiyacınız olmayacaktır. Sadece kodlamada geleceğe yatırım amaçlı yazdım bunları.

- Bir adım daha ilerleyelim, hücresel data ile bağlanacak bir cep telefonu da dışarısıdır WAN üzerindedir.

- Proje testlerini bu şekilde sürdürmenizde sakınca olmaz, ancak projelendirip dışarıdan erişime dahil etmek gibi bir amaç güderseniz PORT kısmı ve dahi bu da yetmeyecek her bir CLIENT'a unique ( tekil / benzersiz ) birer ID üretilmesi / gömülmesi vs. gerekecektir. ( ID'lerin UDP'den broadcast konusundan bahis )

- Amaç kafanızı karıştırmak değil, geri bu başlığa döndüğünüzde arama anahtarlarını vermekti.  Idea

Projelerinizde yolunuz açık olsun. Başarılar.

Bu cevabınızda port kullanımının altını çizmiştiniz. O gün demek istediğinizi tam olarak anlamadığımı fark ettim.
Bahsettiğiniz port numarasının TIdTCPServer default port olduğunu düşünmüştüm. 
Fakat bağlanan her client farklı bir port numarasıyla bağlantı kuruyor / listede farklı bir port numarasıyla görünüyor.
Bu nasıl olabilir? Biz bağlantı sağlanması için sunucu bilgisayarda Windows güvenlik duvarında sadece default port numarasına izin veriyoruz. İzin vermediğim halde clientlar diğer port numaralarıyla nasıl bağlantı sağlıyor olabilirler? 
Client ve sunucu arasındaki veri default port numarası üzerinden olmuyor mu?

İyi çalışmalar.
Öğrenci, EEM, SAÜ.
Youtube
Cevapla
#4
Sondan başlayalım, 

* DefaultPort "sunucu" tarafından bakılırsa dinleme / "istemci" tarafından bakılırsa başvuru portudur. 

- "Sunucu" ve ona bağlanan "istemci" arasındaki ağ bağlantısı, bu iki istasyonu birbirine bağlayan kapalı devre bir kablo gibi düşünürsek, eğer DefaultPort nihai olan olsaydı, üçüncü istemci(ler) sunucuya ulaşamazdı. Çünkü hat meşgul olurdu.

- "Sunucu" dinleme portundan "İstemci" bağlantı isteği yolladığı an boştaki mevcut yeni bir port belirlenir ve o port kullanıma alınarak Client'a atanır. Bağlantı Sunucu bakış açısından bu port üzerinden kuruludur ama İstemci bakış açısından kendisini hangi port ile bağlandığını bilmez. 

Konumuz dışı ama şöyle bir insert yapayım : Kendi projelerimde şu şekilde bir protokol izliyorum. Sunucu ile bağlantı sağlanınca istemcinin ilk Ping çağrısı
<WHOIAM>~

olur. Sunucu da İstemciye aşağıdaki şekilde bir cevap döndürür.

<WHOIAM>|Kullanıcı|192.168.0.34|1234|02.05.2020 12:31:30|{E9985663-B9F4-4632-81B9-F2C064CB764B}|030740A4|121052093~
şeklinde Sunucu tarafındaki veritabanındaki Kullanıcı adı, IP, Port, BağlantıTarihSaati, Unique ID olarak bir GUID, Socket için açılan TheradAdr adresi kısaca aşağıdaki Type record oluşturulup geriye gönderilir. ( en sondaki de TickCount  olarak son veri alışverişi ki periyodik Ping için referanstır) 

type
 TInfo = record
   Name      : String;
   IP        : String;
   Port      : Integer;
   Connected : TDateTime;
   UniqueID  : String;
   Thread    : Pointer;
 end;
  LastSendRecv    : TIdTicks;
 
* Velhasıl böylece İstemci kim olduğunu öğrenmiş olur.


* Konumuza dönecek olursak, sunucuya başka bir client bu sayede başvurup bağlanabilir ve yeni bir port ile thread açar ve yayın hayatına başlar.

* Eğer client de belirli bir portu kullansın isterseniz ona da bir dinleme görevini TCPServer bileşeni atıp defaultport vererek uygun dilediğiniz portu verebilirsiniz.

** Şimdi neden KeepAlive çalışmıyor konusuna gelelim. 
- Buraya kadar Sunucunun aslında iyi bir dinleyici olduğunun altını çizmeye çalıştım. Tek zaafı vardır konuşmayı bilmez.  Wink 
- Daha önceki mesajımda altını çizdiğim konu da bunun içindir. Bu yapıyı sizin kurmanız gerekiyor. ping~ / pong~ yapısını bir şekilde kurmalısınız. 
- Yukarıda verdiğim örnekte her ping~ / pong~ sonunda başarılı diyalog olduğundan zaman damgasını kayıt altına alıyoruz. Periyodik olarak sunucuya ping gelmezse sokete hiç bakmadan zaman aşımı varsa gözünün yaşına bakmadan direkt thread'i sonlandırıyoruz. 

- İstemciniz kurallı bir şekilde Disconnect oluyorsa sorun olmaz ama kabloyu çeker gibi halt/terminate  ediyorsa ne yapsın sunucu...

Başarılar.
Saygılarımla
Muharrem ARMAN

guplouajuixjzfm15eqb.gif
Cevapla
#5
esasında tcp/ip nin kendisi bağlantıyı validate eder, paketlerin gönderilidiğinden emin olur, ack paketleri gönderir, buradaki unutulan noktada ping pong da bir yöntemdir, ancak sayın Muharrem beyinde söylediği gibi ethernet kablosu ansızın çekildi ise 2 tarafda bağlantıyı bilmez, ancak
2 tarafda örneğin 60sn debir tcp üzerinden örneğin chr(13) yani boş bir paket gönderirse(karşı tarafın buna cevap vermesin gerek yok) tcp stack bağlantının validasyonunu otomatik olarak yaparak ortalama 3sn içinde uygulamaya connection gracefuly reset mesajı döndürür, karşıya rst paketi gider, tabi 2 tarafda sürekli birbirinden komut beklediği için data gelmediğinden ötürü tcp stack bu işlemi yapmaz, en sonunda işletim sistemi zamanı aşımı devreye girerek soket kapatılır, bu süre os den os değişmekde saatler/günler seviyesinde olabiliyor.
Cevapla
#6
Yazacaklarımin şu anki sorunuzla alakası yok. Zaten gerekli cevapları arkadaşlar vermiş.

İlerleyen zamanlar için projeniz internete açılacaksa yani wan üzerinden bu işleri yapacaksanız güvenlik için SSL bağlantıyı aklınızda bulundurun.

Ayrıca gömülü yazılım tarafında delay zaman gecikmeleri için mcu desteklediği timer interrupt kullanmaya özen gösterin. Bu şekilde kilitlenmeleri onlersiniz. Bu timer interrupt işlemi delphi thread gibi düşünebilirsiniz.

Arduino interrupt işine ne kadar müsade ediyor bilmiyorum. Bu işlemleri daha rahat gömülü c derleyiciler ile yapabilirsiniz
PostgreSQL - Linux - Delphi, Poliüretan
WWW
Cevapla
#7
ben istek için yeni bağlantı oluşturup veri alındı ise disconnet yapıyorum. bu şekilde kullanman arka arka gelen istekler için bir yavaşlık veya başka sıkıntı olabilir mi
Cevapla
#8
(28-02-2021, Saat: 00:45)mrmarman Adlı Kullanıcıdan Alıntı: Sondan başlayalım, 

* DefaultPort "sunucu" tarafından bakılırsa dinleme / "istemci" tarafından bakılırsa başvuru portudur. 

- "Sunucu" ve ona bağlanan "istemci" arasındaki ağ bağlantısı, bu iki istasyonu birbirine bağlayan kapalı devre bir kablo gibi düşünürsek, eğer DefaultPort nihai olan olsaydı, üçüncü istemci(ler) sunucuya ulaşamazdı. Çünkü hat meşgul olurdu.

- "Sunucu" dinleme portundan "İstemci" bağlantı isteği yolladığı an boştaki mevcut yeni bir port belirlenir ve o port kullanıma alınarak Client'a atanır. Bağlantı Sunucu bakış açısından bu port üzerinden kuruludur ama İstemci bakış açısından kendisini hangi port ile bağlandığını bilmez. 

Konumuz dışı ama şöyle bir insert yapayım : Kendi projelerimde şu şekilde bir protokol izliyorum. Sunucu ile bağlantı sağlanınca istemcinin ilk Ping çağrısı
<WHOIAM>~

olur. Sunucu da İstemciye aşağıdaki şekilde bir cevap döndürür.

<WHOIAM>|Kullanıcı|192.168.0.34|1234|02.05.2020 12:31:30|{E9985663-B9F4-4632-81B9-F2C064CB764B}|030740A4|121052093~
şeklinde Sunucu tarafındaki veritabanındaki Kullanıcı adı, IP, Port, BağlantıTarihSaati, Unique ID olarak bir GUID, Socket için açılan TheradAdr adresi kısaca aşağıdaki Type record oluşturulup geriye gönderilir. ( en sondaki de TickCount  olarak son veri alışverişi ki periyodik Ping için referanstır) 

type
 TInfo = record
   Name      : String;
   IP        : String;
   Port      : Integer;
   Connected : TDateTime;
   UniqueID  : String;
   Thread    : Pointer;
 end;
  LastSendRecv    : TIdTicks;
 
* Velhasıl böylece İstemci kim olduğunu öğrenmiş olur.


* Konumuza dönecek olursak, sunucuya başka bir client bu sayede başvurup bağlanabilir ve yeni bir port ile thread açar ve yayın hayatına başlar.

* Eğer client de belirli bir portu kullansın isterseniz ona da bir dinleme görevini TCPServer bileşeni atıp defaultport vererek uygun dilediğiniz portu verebilirsiniz.

** Şimdi neden KeepAlive çalışmıyor konusuna gelelim. 
- Buraya kadar Sunucunun aslında iyi bir dinleyici olduğunun altını çizmeye çalıştım. Tek zaafı vardır konuşmayı bilmez.  Wink 
- Daha önceki mesajımda altını çizdiğim konu da bunun içindir. Bu yapıyı sizin kurmanız gerekiyor. ping~ / pong~ yapısını bir şekilde kurmalısınız. 
- Yukarıda verdiğim örnekte her ping~ / pong~ sonunda başarılı diyalog olduğundan zaman damgasını kayıt altına alıyoruz. Periyodik olarak sunucuya ping gelmezse sokete hiç bakmadan zaman aşımı varsa gözünün yaşına bakmadan direkt thread'i sonlandırıyoruz. 

- İstemciniz kurallı bir şekilde Disconnect oluyorsa sorun olmaz ama kabloyu çeker gibi halt/terminate  ediyorsa ne yapsın sunucu...

Başarılar.

Merhaba,

Zaman ayırıp detaylı cevap yazdığınız için teşekkür ederim. TCP iletişim sisteminde portların nasıl bağlantı kurduklarını anlamış oldum.
Tüm bağlantı işleminin default port üzerinden yapılmış olduğunu düşünüyordum. Doğrusunu öğrenmem sistemin çalışmasını anlamamda çok faydalı oldu. 

(28-02-2021, Saat: 00:45)mrmarman Adlı Kullanıcıdan Alıntı: - İstemciniz kurallı bir şekilde Disconnect oluyorsa sorun olmaz ama kabloyu çeker gibi halt/terminate  ediyorsa ne yapsın sunucu...

(28-02-2021, Saat: 00:52)hayalyilmaz43 Adlı Kullanıcıdan Alıntı: ancak sayın Muharrem beyinde söylediği gibi ethernet kablosu ansızın çekildi ise 2 tarafda bağlantıyı bilmez

onDisconnect olmadığını ilk defa mega 2560'lara yeniden kod yüklediğimde fark etmiştim. Bundan sonraki denemelerimde tam dediğiniz gibi kabloyu söküp takarak yaptım.  Tongue

Arduino kodu içerisinde loop içinde hiçbir şekilde Disconnect yapmıyorum tam aksine sürekli Disconnect olup olmadığını kontrol edip olmuşsa tekrar Connect yapıyorum.

  if (!client.connected()) { //client'ın bağlantısı yoksa bağlantı kopmuşsa

(28-02-2021, Saat: 12:01)3ddark Adlı Kullanıcıdan Alıntı: Yazacaklarımin şu anki sorunuzla alakası yok. Zaten gerekli cevapları arkadaşlar vermiş.

İlerleyen zamanlar için projeniz internete açılacaksa yani wan üzerinden bu işleri yapacaksanız güvenlik için SSL bağlantıyı aklınızda bulundurun.

Ayrıca gömülü yazılım tarafında delay zaman gecikmeleri için mcu desteklediği timer interrupt kullanmaya özen gösterin. Bu şekilde kilitlenmeleri onlersiniz. Bu timer interrupt işlemi delphi thread gibi düşünebilirsiniz.

Arduino interrupt işine ne kadar müsade ediyor bilmiyorum. Bu işlemleri daha rahat gömülü c derleyiciler ile yapabilirsiniz

Arduino tarafında delay komutundan vazgeçerek yerine millis kullanarak işlemci bloklanmasının önüne geçtim. Arduino serisi işlemciler için başka bir timer kullanımı var mı, bilmiyorum. Arduino yerine stm32 ile ethernet bağlantısı kurmak herkes gibi benim de hedeflerim arasında konu hakkında kaynak az , işlem zor ve tecrübe gerektiriyor. Stm32 sistemin arduino'ya göre daha detaylı kodlama yapma şartı işimizi zorlaştırıyor. Evde Stm32F407 Discovery kartım vardı. Arza yapmadan önce örneklere bakarak adc okuması ve Uart iletişimi yapmıştım. 

Başka bir konu altında verdiğiniz tcp client server örneği çok faydalı oldu.  Sizin için 5-10 dkda hazırlanabilecek bir örnek benim gibi yeni başlayanlara 5-10 günlük vakit kazandırıyor. 

(28-02-2021, Saat: 12:51)nguzeller Adlı Kullanıcıdan Alıntı: ben istek için yeni bağlantı oluşturup veri alındı ise disconnet yapıyorum. bu şekilde kullanman arka arka gelen istekler için bir yavaşlık veya başka sıkıntı olabilir mi

Benim sistemim iki adet mega 2560 bir adet esp32 ve bir adet telefon bağlantısından oluşuyor. Mikroişlemciler her 150-300 milisaniyede bir veri gönderiyor. Client sayısı az veri trafiği yoğun bir şekilde çalışıyor.

İyi çalışmalar.
Öğrenci, EEM, SAÜ.
Youtube
Cevapla


Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  TIdTCPServer'dan seçili client'a veri gönderme [çözüldü] Yasemin 12 5.239 24-11-2020, Saat: 09:27
Son Yorum: pro_imaj



Konuyu Okuyanlar: 1 Ziyaretçi