> Delphi | Indy Kütüphanesi | Network Programlama > Indy ile Network Programlama 3 – Server

Indy ile Network Programlama 3 – Server

Bundan önce, gerek client tarafında gerekse server tarafında okuma ve yazma işlemlerinin nasıl yapıldığını gördük. Ayrıca gönderilecek verinin küçük ve fazla miktarda olması durumunda bağlantıyı yavaşlatmamak için yapılması gereken işleri de görmüş olduk. Bu makalemizde Indy ile bir TCP Server’ı nasıl tanımlayabileceğimize bakacağız. Server oluşturma modellerini incelemeye çalışacağımız gibi bir server oluştururken takip edilen yolları da görmeye çalışacağız. Ve bir örnek uygulama ile makalemizi bitireceğiz.

Bölüm 1 – Giriş

Bölüm 2 – Okuma ve Yazma

Bölüm 3 – Server

TIdTCPServer

En önemli Indy server, TIdTCPServer bileşenidir. TIdTCPServer, programın ana kanalından bağımsız olan ikincil bir dinleme kanalı oluşturur. Dinleme kanalı, client’den gelen istekleri bekler. Cevap verdiği her bir Client bağlantısı için ayrı birer kanal oluşturur. Daha sonradan ilgili olaylar kanalın içeriği ile beraber tetiklenir.

Å?ekil1: Indy TCP Server Kanal Yönetimi

Kanalların Rolleri

Indy server’ları, kanallar etrafında dizayn edilmiştir ve Unix server’ların çalışma mantığına yakındır. Yanlız Unix uygulamaları tipik olarak ya hiç soyutlama katmanı olmadan ya da az miktarda soyutlama katmanı ile direk olarak stack ile yüz yüze gelir. Indy ise, programcıyı stack ile uğraşma karmaşasından soyutlayarak, detayları otomatik ve şeffaf bir şekilde kendi içinde tanımlar.

Genelde Unix server’ları, client’lardan gelen istekleri izleyen bir ya da bir kaç dinleyici işleme sahiptir. Dinleyici işlemin kabul ettiği her client isteği, server tarafından yeni bir fork işlemine alınır. Her bir işlem bir client ile ilgili olduğu için, server birden çok client bağlantısını çok kolay bir şekilde tutabilmektedir. Tabi ki her işlem, kendi güvenlik kapsamında çalışmaktadır.

Indy serverları da benzer biçimde işlemleri gerçekleştirir. Unix’in aksine, Windows fork’lar ile iş görmez. Bunun yerine benzer ama aynı olmayan kanalları işler. Unix’in bir iş ayırmasının aksine Indy server’ları, her bağlantı için ayrı bir kanal ayırır. Bu işlemlerin dezavantajından çok, hemen hemen tüm avantajlarını sağlar.

Yüzlerce Kanal

Çok meşgul bir server’da, binler belki onbinlerce kanal gerekebilir. Yaygın bir inanışa göre, kanlların fazlalığı sisteminizi öldürür. Ama bu yanlış bir tespittir.

Å?u an bile sisteminizde çalışan thread sayısı sizi şaşırtabilir. Mesela bazen olurki 300 adet kanal açık olduğunda işlemci ve hafıza kullanımı, 500 adet kanal açık olmasına göre daha fazla olabilir.

Çoğu server’lar kanallarda belli verileri beklemekle harcarlar. Bu kanalların mesela 500 tanesinin ancak 25-30 tanesi aktif olarak çalışıyorlardır. 500 tane kanal açık olması demek, 500’ünün de aynı anda çalıştığı manasına gelmez.

Socket uygulamalarında, yavaş çalışan ekipmanlar yüzünden bazı kısıtlamalar olmaktadır. Bu ekipmanlar birisi hiç şüphesiz ethernet kartlarıdır. Ver hiç bir şey ethernet kartının mevcut kapasitesinin üzerine çıkamaz. Hızlı bir ethernet dahi olsa, işlemcinin cevap verememesi gibi bir çok nedenden dolayı darboğazlar(bottleneck) yaşanabilir.

Gerçekçi Kanal Limitleri

Bir işlemin 1000’den fazla kanal oluşturmasını ortalama bir sistem, hafıza meselelerinden dolayı bir sorun olarak algılar. Kanalların stack boyutu düşürülerek kanal sayısı artırılabilir. Bununla birlikte bu noktada başka alternatifleri araştırmak gerekmektedir.

Çoğu server, olsa olsa bir kaç yüz kanala ihtiyacı olur. Ama yüksek seviye serverlar, daha fazlasına ihtiyacı olur ve bu yüzden farklı davranışlara yönelmelidir. Indy’yi kullanan serverlar böyle binlerce kanal oluşturan tiplerdendir.

Anlaşılması gereken diğer bir nokta ise, servera bağlı client sayısı, o an ki kanal sayısına eşit olmak zorunda değildir. Her client kendi özel kanalını ayırmasının yanında, client sadece bağlıyken bir kanal ayrılır. HTTP server gibi çoğu server servisinin bağlantısı kısa ömürlüdür. Web sayfaları için HTTP bağlantıları, sadece son bir saniye ya da daha azında işlem görür. Sadece 500 kanal ve her bağlantı başına 1 sn harcandığını farz edersek, saatde 30,000 client’a izin verir.

Indy 10, server kanallamasına ek olarak diğer modelleri de destekleyerek, bu ihtiyaçlara cevap veriyor. Indy 10, sadece kullanılarbilir hafıza kadar socketleri ayırabilir.

Server Modelleri

TCP Server oluşturabilmek için iki yol mevcuttur. Komut Denetleyicileri(Command Handlers) ve OnExecute olayı. Komut denetleyicileri server oluşturmayı çok kolaylaştırmaktadır, ama her duruma uygun değildir.

Komut denetleyicileri, yazı formatında komut alışverişi yapan protokoller için uygundur. Fakat binary yapıda komutları olan veya komutlar üzerine kurulu olmayan protokoller için uygun değildir. Çoğu protokol yazı tabanlıdır ve komut denetleyiciler kullanılabilir. Komut denetleyicileri tamamen opsiyoneldir. Kullanılmadığında, Indy serverları hala eski metod kullanımlarını destekler. Komut denetleyicilerini daha sonra ayrıntılı bir şekilde işleyeceğiz.

OnExecute Olayı

Bazı protokoller, binary komutlar içerir veya hiç komutları yoktur. Bu yüzden komut denetleyicileri için uygun değillerdir. Bu tip serverlar için OnExecute olayı kullanılmalıdır. OnExecute, bağlatı sürdürüldüğü müddetçe, tekrar tekrar meydana gelir. Å?imdi basit bir server tanımlamasını Indy 10 için görelim.



		

 

Indy 10’dan önceki versiyonlarda, OnExecute olayının parametresi AContext değil AThread‘dir. Ve WriteLn, ReadLn gibi komutlar, önceki makaleden de hatırlayacağınız gibi, IOHandler içinde değil direk olarak TIdConnection içindedir. Yani Indy 10 için yazdığımız yukarıdaki with bloğunu şöyle yazmalıyız:



		

Uygun bir bağlantı olup olmadığını test etmemize gerek yoktur. Çünkü Indy bunu otomatik olarak yapmaktadır. Ayrıca herhangibir döngü de yapmanıza gerek yoktur, çünkü bunu da Indy sizin yerinize yapar. Bu olay tekrar tekrar çalıştırılır, ta ki bağlantı var olmayana kadar. Buna sebep olan yukarıda ‘CIKIS’ komutunda olduğu gibi ne harici bir bağlantı kesilmesidir, ne bir network hatasıdır, ne de client’ın bağlantıyı kesmesidir. Gerçekte, Indy’nin bağlantı kesme denetimi ile müdahale edilmesiyle hiçbir döngü yapılmaz hale gelecektir.

Komut Denetleyicileri (Command Handlers)

Indy 9.0 ile birlikte gelen bu özellik, TCP server tanımlamalarını çok kolaylaştırmıştır. Komut denetleyicilerini, server’ın "Action List"’i olarak görebilirsiniz. Her bir komut için komut denetleyicisi tanımladıktan sonra, bu komut denetleyicisinin davranışını ve de cevabını da belirtmelisiniz. Client tarafından bir komut geldiği vakit, server bu komutu ayrıştırır ve bağlı olan komut denetleyicisine yollar. Komut denetleyicilerinde sadece davranışlarını düzenleyebileceğiniz özellikler yoktur. Ayrıca metodlar ve olaylar da bulunmaktadır.

Önceden de bahsettiğimiz gibi komut denetleyicileri sadece yazı tabanlı komut ve cevaplara sahip TCP protokollerinde iş görür. Bununla birlikte, günümüzde kullanılan protokollerin %95’i bu şekildedir. Komut denetleyicileri binary verilerle uğraşabilirken, sadece yazı tabanlı komutlarla uğraşabilir.

Indy 9’da TIdTCPServer‘da mevcut olan CommandHandlers özelliği, Indy 10 ile birlikte artık TIdCmdTCPServer sınıfına alınmıştır. Yani eğer komut denetleyicilerini kullanmak istiyorsanız, TIdTCPServer yerine TIdCmdTCPServer bileşenini kullanmalısınız. Yine Indy 9’da CommandHandlersEnabled isminde bir özellik mevcuttur. Eğer bu özellik True ise, öncelik komut denetleyicilerinde olacaktır. Yani OnExecute olayı olsa bile, bu özellik True olduğunda ilgili komut denetleyicisi çalıştırılacaktır, OnExecute çalıştırılmayacaktır. Ama komut denetleyicisi ya aktif değil ya da bilinmeyen bir komut ise OnExecute çalıştırılır. Indy 10’da komut denetleyicileri ayrı bir bileşene taşındığından bu özelliği gerek kalmamıştır.

Å?imdi şöyle bir örnek yapalım. Üç adet komutumuz olsun:

  • TarihGoster: Server’ın tarihini göstersin.
  • Yardim: Mevcut komutları göstersin.
  • Cikis: Oturumu kapatsın ve bağlantıyı kapatsın.

Bu basit tanımlama için yeni bir uygulama oluşturalım. Foruma bir adet TIdCmdTCPServer yerleştirelim. DefaultPort olarak herhangi bir şey girebilirsiniz. Örneğimizde 8500 gibi bir port kullanalım. Active özelliğini True yapalım, böylece uygulama çalışır çalışmaz TCP server çalışmaya başlayacak.

Bileşenin CommandHandlers özelliğine çift tıklayalım. Açılan özellik editöründen yeni bir komut denetleyicisi ekleyelim. Oluşturduğumuz yeni denetleyiciyi seçtikten sonra Object Inspector’dan özelliklerini değiştirebilirsiniz. Command özelliğini Cikis olarak değiştirelim. Eğer server’ı sadece sizin yapacağınız program kullanacaksa ve sistem her zaman Türkçe karakterleri tanıyabilecek nitelikte olacaksa, Command kısmına Türkçe karakter girebilirsiniz. Sonuçta bu da bir stringdir. Ama aksi durumda sorun çıkarmaması için Türkçe karakterden kaçınmalısınız.

Disconnect özelliğini True yapalım. Böylece bu komut çalıştığında oturum kapatılacak ve client ile bağlantı kesilecektir. Name özelliğini bu örneğimiz için cmdCikis olarak değiştirelim. NormalReply altından Code kısmına 200, Text kısmına "Gule gule" yazalım. Komutlar genelde, 3 haneli bir sayı ve isteğe bağlı bir yazıdan oluşur. Eğer hata oluşmazsa, komutun normal cevabı bu girdiğimiz code ve text olacaktır.

Bu şekilde çalışabilecek bir komut denetleyicisi oluşturmuş olduk.

Å?imdi bu komutumuzu test edelim. Derleyip programı çalıştırdıktan sonra, komut satırından "telnet 127.0.0.1 8500" şeklinde girelim. Karşımıza "200 Welcome" gibi bir yazı çıktı ise başarılı bir şekilde bağlanmışız demektir. Ardından az önce eklediğimiz Cikis komutunu yazalım ve entere basalım. Gelen cevap NormalReply kısmına yazdığımız text ve code’dur. Tekrar telnet ile bağlantı kurup Cikis dışında bir şey girdiğinizde "400 Unknown Command" gibi bir cevap ile karşılaşırsınız.

Tebrikler ilk komut denetleyici tabanlı tcp server’ınızı oluşturdunuz. Å?imdi dilerseniz Yardim ve TarihGoster komutlarına da el atalım.

Cikis komutunu tanımladığımız gibi Yardim komutunu da tanımlayalım. Tek fark olarak, Disconnect özelliği False olmalıki, bu komut çalıştıktan sonra bağlantımız kesilmesin. NormalReply.Text özelliği StringList özellik editörünü kullandığı için bibr Memo’nun ya da ListBox’ın içeriğini doldurur gibi komut yardım bilgilerini yazabilirsiniz.

TarihGoster’i de Yardim komutu gibi tanımlıyoruz. Tek fark, NormalReply.Text özelliğini tanımlamıyoruz. Ama Code‘u yine 200 yapalım. TarihGoster komut denetleyicisi seçili iken Object Inspector’dan yeni bir event ekleyelim. OnCommand event’ına gelip çift tıklayalım. Eğer TIdCommand bulunamadı gibi bir hata alırsanız uses kısmına IdCommandHandlers‘ı ekleyebilirsiniz. Event Handler’ımızın içeriğini aşağıdaki gibi dolduralım



		

Eğer komutumuzda ekstradan bir parametre yoksa kendimiz bir tarih formatı belirliyoruz. Aksi halde client’ın gönderdiği ilk parametreyi tarih formatı olarak kullanıyoruz. Ardından cevabımızı "Text" StringList’inin içine yerleştiriyoruz.

Bu makalemizde de sona geldik. Umarım faydası olmuştur. Bu 3 makale ile Indy’ye güzel bir giriş yaptığınızı düşünüyorum. Eğer vakit olursa Indy’de daha ileri konulara da el atmaya çalışacağız. Fikir ve eleştirilerinizi bekliyorum.

vesselam.

Fatih Tolga Ata © 2007

Kaynaklar:

Bölüm 1 – Giriş

Bölüm 2 – Okuma ve Yazma

Bölüm 3 – Server

» Tags: , , , , , , , , , , , ,

10 Comments

  • At 2007.08.04 09:32, ikutluay said:

    Merhaba çok güzel makaleler. Açıkçası PHP kitabımı online hale getirdikten sonra bende bu tip makaleleri blogumda yer vermeyi düşünüyorum. Ama bünler bilgi düzeyi olarak çok çok iyi.

    belki bakmak istersin..
    http://www.ibrahimkutluay.net
    http://www.ibrahimkutluay.net/blog

    Yazılar henüz hazırlama aşamasında. açıksası HTML ile yapsam yoksa wiki tarzı birşey mi yapsam blogdamı yapsam karar veremedim.

    • At 2007.08.04 10:16, Fatih Tolga Ata said:

      Tasarım çok hoş. İçerik de bence iyi. Aslında benim dileğim bu tarz blogların ve sitelerin artması. Türkiyede programlama çok iyi düzeyde gitmiyor bence. İngilizce olarak bir çok konuda bilgi bulabiliyoruz. Bence Türkçe de bu bilgilere layık bir dil. Biz üç arkadaş’ın Diyezon’u açmasının en büyük gayelerinden birisi de bu şüphesiz.;
      Aslında sırf programlama ile ilgili, tüm blogları rss olarak bünyesinde toplayan bir site yapılmalı. Bu, bu tarz sitelerin artmasında büyük etken olacaktır diye düşünüyorum. CodeGear’ın bu tarz bir sistemi var:
      http://blogs.codegear.com/team/delphi
      Bir de bence siteden çok içindeki bilgi ile uğraşın. Bu yüzden wordpress tarzı sizi fazla yormayacak bir şey kullanmanız tavsiyemdir.
      Vesselam.

      • At 2007.08.04 10:22, Fatih Tolga Ata said:

        Ayrıca özellikle php ve webtasarımı noktasında bence yazıdan çok video tarzı tutorial’ler daha faydalı oluyor. Ama tabi Youtube gibi bir şey kullanılmayacaksa, iyi bir hosta ihtiyaç var bunun için. http://www.asp.net ve http://ajax.asp.net sitelerinde bu tarz bir uygulama sözkonusu.

        • At 2007.08.06 14:22, ibrahim kutluay said:

          Merhaba

          Site zaten template… Dileklerimiz ortak… Bende zaten PHP kitabı yazanlara isyan gibi başladım buna. İlerde Delphi içinde birşeyler düşünüyorum. Birebir olmasada konular geldiğinde makalerinden esinlenme yapabilirim. (Kaynak belirtirim tabii)..

          Genede bir konu zoruma gidiyor. Şimdi biz içerik için kendimi yırtıyoruz bazıları copy paste yapıp alıntı kaynak şu bu hiç birşey belirtmeden, birde utanmadan altına adını yazarak orda burda yayınlıyor.

          Sitemdeki blog kısmı zaten wordpress…

          Php konusunda gelince kitaı siteye koydupumda göreceksinki bu kapsamda bir basılı kaynak daha önce çıkmamış. Kalite şu bu çok iyi mükemmel demiyorum. Ama bir sürü örnek kod var. Öneri var. Fonksiyon listesi var. NEyi neden yaptığı açıklanmış. İşlem mantığı açıklanmış. Vt dizaynına dokunmuşum.

          Yani böylede olması gerek çünkü kar etmek derdim yoki sayfa sayısı derdim yok, baskı, yayınevi şu bu derdim yok. Ben iyiliği yapıp denize atıyorum.

          Elimde delphi konusunda 40000 den fazla ipucu toplanmış durumda. yakında

          http://www.ibrahimkutluay.net/blog

          buradan yada kendi yazacağım bir vt scripti ile yayınlayacağım azar azar. çünkü zaman yok ve malum birde ekmek derdi var.

          • At 2007.12.19 00:45, Şaban Yerli said:

            Çok güzel bir makale, teşekkürler. Fakat FTP’ nin daha iyi bir bağlantı şekli olduğu söyleniyor. Gerçeklik payı var mıdır?

            • At 2008.02.05 15:32, Hasan Danışık said:

              Merhabalar

              Öncelikle bu konuda Türkçe kaynak bulmak beni oldukça sevindirdi. Açıkcası ben TServerSocket nensnesini kullanarak bir program yazdım ama 10054 nolu hata kodunu alıyorum sürekli ve hatayı handle edemiyorum. Bu yüzden Indy e geçmeye karar verdim. Indy de multithread olarak taleplere nasıl cevap verebiliriz ? Onexecute olayında AThread parametresini başka bir thread demi yönlendirmemiz gerekli ? Eğer yardımcı olursanız çok makbule geçecek.

              • At 2008.10.20 12:09, Uğur Parlayan said:

                Sanırım bu, 1. ve 3. makale de dahil olmak üzere tam olarak ifade edilememiş bir konu var.

                Diyelim ki bir TCP Server var, bu server bir veritabanından o an bağlı olduğunu bildiği x ve y clientlerine bir string gönderecek ama q ve z clientleri de bağlı. böyle bir durumda server sadece x’e veri göndereceği zaman ne yapmalı? bunu mevcut onExecute olayıyla yapmaya kalkarsa başaramaz. TIDTcpServer kullanılarak hariçten, bağlantı bilgileri bilinen tek bir cliente nasıl veri gönderileceği tam olarak belli değil, özellikle INDY 10’da…

                • At 2009.03.26 15:54, Muhammet said:

                  Merhabalar,

                  Öncelikle böyle güzel, içeriği dolu bir site için sizi kutlarım.

                  Benim bir sorum olacak:

                  indyTcpServer kullanarak yaptığımız TCP Server programı var. Server Programı kendisine bağlanan cihazları bir listede tutuyor ve bağlı cihazlar üzerinde bazı işler gerçekleştiriyor. İstemci makinalar bazen sıradışı bir olay nedeniyle (örn. Elektrik kesilmesi) kapanıyorlar, Server programı ise bunu anlyamıyor… bundan dolayı Server programının bağlantı listesinde ilgili istemci bağlı gözüküyor ve bir süre sonra liste kabarmaya başlıyor. Çözüm olarak belli aralıklarla bağlantıları sorgulamayı, bağlı olmayan cihazları bağlantı listesinden kaldırmayı düşündük. Sorgulama yöntemi şöyle: Server Cihazlara Özel bir Kelime gönderiyor (‘BAÄžLIMI?’) gibi, ve istemcilerden doğru cevabı almayı bekliyor (‘EVET!’ gibi). Belli bir süre istemciden cevap gelmez ise, ilgili bağlantıyı listeden siliyor.

                  Yukarıda bahsettiğim sorgulama için bir Timer kullandık, timer belli aralıklarla (örn: 1 dk) sorgulama yapıyor. Sorun şu ki: kısa aralıklarla sorgulama yaptığımızda herşey normal örn 1 dk. Ancak uzun aralıklarla sOrgulama yapıldığında Örn. 5dk; istemciye gönderdiğimiz sorgulama cümlesi gerçekte istemciye gitmiyor. Not: Program hata vermiyor ancak sitemci tarafına da gelen birşey yok. Hal böyle olunca Server Programı sorgulama cümlesini gönderdim zannediyor ve cevap bekliyor, cevap gelemeyeceği için de bir süre sonra bağlantıyı listeden siliyor. Acil yardımınızı rica ediyorum, Teşekkürler İyi Günler.

                  • At 2009.03.30 19:57, Fatih Tolga Ata said:

                    Açıkcası server tarafında bağlı olan client listesini tutmak gibi bir çalışmam olmadı önceden. Ancak yaptığınız handshake vari mesaj gönderimi ve alımı mantıklı olmuş. Bence mesajın gitmemesi timer süresi ile alakalı değil. Belli bir süre zarfında mesajın gitmesini engelleyen bir sorun meydana geliyordur. Bu sorunun üstesinden gelebilmek için hem client hem de server makinada ortak debug yapmalısınız. Bunun için “remote debug” yöntemini kullanabilirsiniz. Hatta bence fiziksel bir ağ yerine vmware gibi sanal bir ortam kullanın. Çünkü ağın fiziksel donanımlarında da sorun olabilir. Sormama gerek yok ama tcpclient, programın ana kanalında çalışmıyor herhalde… Eğer ayrı kanalda ise debug yaparken belli bir süre sonunda kanalların yok olup olmadıklarına bakın. Ayrıca tcpserver aktif olduktan itibaren ve mesaj gönderim alımlarında oluşan bütün kanalaları kontrol edin. Eğer server ve client tarafında kanallar uygunsuz bir şekilde sonlanıyorsa bunların neden sonlandığını araştırın. Pek yardımcı olamasam da şu an için aklıma bunlar geliyor.

                    Kolay gelsin.

                    • At 2012.06.07 00:55, Yaşar Aktaş (Shurzan) said:

                      Merhaba, Vermiş olduğunuz bilgiler çok faydalı oldu.
                      Devamın gelmesi dileğiyle.

                      Teşekkürler

                      (Required)
                      (Required, will not be published)