> Delphi | Indy Kütüphanesi | Network Programlama > Indy ile Network Programlama 2 – Okuma / Yazma İşlemleri

Indy ile Network Programlama 2 – Okuma / Yazma İşlemleri

Bundan önceki makalemizde, Indy’nin socket programlamada bize kazandırdıklarını ve ne kadar kolay kullanımda olduğunu gördük. Onca socket kütüphanesi varken ve .NET de socket desteği verdiği halde Indy kütüphanesine niye ihtiyacımız olduğunun cevabını da vermeye çalıştık. Ayrıca Indy’nin desteklediği bir çok programlama modelinden en yaygın olarak kullanılan iki tanesi üzerinde karşılaştırma yaptık ve yanlış fikirleri ortadan kaldırmaya yönelik bazı konulardan bahsettik. Bu makalemizde öncekinin devamı olarak Okuma ve Yazma işlemlerini tanımaya çalışacağız. Indy’nin ne olduğunu bilmiyor ya da network, socket programlamanın zor olduğunu düşünüyorsanız ilk önce buradaki birinci makalemize bir göz atınız.

Bölüm 1 – Giriş

Bölüm 2 – Okuma ve Yazma

Bölüm 3 – Server

İstisnaları Yakalamak(Exceptions)

Önceden de bahsettiğimiz gibi, sanılanın aksine her istisna bir hata değildir. Indy, herhangi bir mesajı hata bile olmasa istisna olarak size sunacaktır. Bunun sebebi, hiç şüphesiz engelleyen socket modeline destek verdiğindendir. Bir çok modele destek veriyorsanız, bunların hepsine cevap verebilecek yapıları seçmelisiniz. Bildiğiniz gibi istisnalar, mevcut program akışını durdur ve mevcut istisnanın kullanıcıya iletilmesini sağlar. Tabi, eğer biz istisnayı kullanıcıya gösterilmeden önce yakalarsak. Bu temel konu, Indy’ye özel bir konu değildir. Bir çok yerde bu konu ile ilgili makaleler yazılmıştır. Biz sadede gelelim ve Indy istisnalarını nasıl yakaladığımızı görelim:



		


Görüldüğü gibi, normal istisna yakalamadan farkı yok. İstisna yakalamaya yabancı olanlar için yukarıdaki kodların açıklaması şöyle. En dıştaki try-except bloğu ile Connect ve Disconnect dahil, yaptığımız bütün işlemlerde istisna gerçekleşmesi dahilinde except bloğunu çalıştıracaktır. Bu istisnaları case/switch gibi ya da if ile ayırt eder gibi, "on" ayrılmış kelimesi ile ayırt ediyoruz. "on" kelimesinden sonra istisnayı tutacak bir değişken ismi ve istisnanın tipini belirtiyoruz. Burada kullandığınız değişkeni önceden var bloğunda ya da başka bir yerde tanımlamanıza gerek yok. İçerideki try-finally bloğu ise, okuma yazma işlemleri bitiminde finally bloğununa dallanılacak ve Disconnect metodu çalışacaktır.

Bu istisnayı kullanabilmeniz için IdException dosyasını, uses bölümüne eklemeniz gerekmektedir. Böylelikle, bu işlem sırasında gerçekleşebilecek normal VCL istisnaları ile Indy istisnalarını ayırmış oluyoruz. Tabi ki daha spesifik istisna tipleri de vardır. Tek yapmanız gereken ne kadar istisna tipini kontrol edecekseniz, ardarda "on" kelimesi ile belirlediğiniz istisna tipini burada kullanmak olacaktır.

Buna ek olarak, eğer Connect metodu esnasında bir hata meydana gelirse, istisna çalışmadan önce Connect metodu kendini temizleyecektir. Tekrar Disconnect yapmanıza gerek yok. İsterseniz fazladan bir try-except bloğu kullanarak, yazma okuma işlemlerindeki istisnaları kontrol edebilirsiniz.

Dediğimiz gibi, istisnalar genel olarak hataları belirtmek için kullanılsa da, bu istisnaların birer hata olduğunu göstermez. Mesela EAbort, istisnası bir hata değildir. Sadece kullanıcının işlemi iptal ettiğini belirten bir istisnadır. Indy içinde de bu şekilde hata olmayan bir çok istisna tanımlanmıştır. Özellikle eğer istisna, EIdSilentException istisnasından türeyen bir istisna ise bunlar hata olmazlar. Mesela genellikle karşılaşılan istisna tipi olan EIdConnClosedGracefully istisnası, EIdSilentException‘dan türemiştir. Hangi exception’nın hangisinden türediğini bulabilmek için yardım dosyalarında istediğiniz istisna tipine göz atabilirsiniz.

TIdAntiFreeze

Indy, kullanıcı arayüzü üzerindeki donmaları çözmek için özel bir bileşene sahiptir. Uygulama içindeki tek bir TIdAntiFreeze bileşeni, uygulamanın ana kanalı içinde meydana gelebilecek kullanıcı arayüzünün donmasını engelleyecektir.

Engelleme çağrıları sadece ana kanalda çalışan kullanıcı arayüzünü dondurur. Bu yüzden TIdAntiFreeze, sadece ana kanaldaki kullanıcı arayüzünün donmasını engeller. Eğer Indy’yi başka kanallar içinde kullanıyorsanız, TIdAntiFreeze kullanmanıza gerek kalmayacaktır. Çünkü Indy başka bir kanalda çalıştığından, ana kanaldaki arayüzü dondurmayacaktır. Zaten, TIdAntiFreeze sadece ana kanalı etkilediğinden, Indy için açacağınız diğer kanallarda bu bileşeni kullanmak gereksiz olacaktır.

Bazı durumlarda TIdAntiFreeze, socket iletişimini yavaşlatacaktır. TIdAntiFreeze bileşeninin özellikleri ile, önceliğin ne kadarının uygulamaya ve ne kadarının socket iletişimine verileceğini ayarlayabilirsiniz. Uygulama ve iletişimin önceliği meselesinden dolayı, eğer bu bileşeni kullanıyorsanız, iletişimi yavaşlatmamak için OnClick, OnPaint, gibi eventlarda fazla süre harcamamalısınız. Bu yüzden bu bileşeni kullanmak tamamen programcının kontrolündedir.

Okuma ve Yazma

Indy, verileri yazıp okumak için bir çok değişik ihtiyaca cevap veren metodları desteklemektedir. Bu metodlar TIdTCPConnection ile ilgilidir (Özel olarak belirtmedikçe, bu makale boyunca bahsedilecek bağlantı çeşiteri TCP üzerinden olacaktır). Bu sınıftan türeyen bütün client ve server bileşenleri de doğal olarak bu metodları desteklemektedir.

Eğer benim gibi Indy 10 kullanıyorsanız, ne TIdTCPClient, ne de TIdTCPServer üzerinde bahsedeceğimiz bu metodları bulamayacaksınız. Çünkü okuma ve yazma işlemleri Indy 10 ile birlikte IOHandler isminde harici bir sınıfa taşındı. Yani önceden TIdTCPConnection.Read olan bir komut artık TIdTCPConnection.IOHandler.Read şeklinde kullanılacak.

Hatırlayacağınız üzere Indy, engelleme üzerine çalışmaktadır. Bu yüzden, gelen isteklere cevap verebilmek için herhangi bir olay(event) aramayınız. Metod, işlemini bitirmeden asla geri dönmeyecektir. Eğer istenilen işlem bitirilemez ise, bununla ilgili bir istisna meydana gelecektir.

Bu makale, tüm okuma ve yazma metodlarından bahsetmek için biraz küçük kalır. Daha fazlası için Indy yardım dosyalarına göz atmalısınız.

ReadLn, ReadInteger, ReadCardinal, ReadSmallInt, ReadString vs… :

Bu tip metodlar çıktı olarak, kullandığınız veri tipine göre çıktı verir. Eğer karşı taraf, size Integer tipinde bir veri yollamış ise, siz bunu ReadInteger ile okumalısınız. Aynı şekilde 32-bit işaretsiz bir sayı yollamış ise ReadCardinal‘i kullanmalısınız. Burada kafanızı karıştıracak iki metod vardır. Birisi ReadLn ve diğeri ReadString. Aslıda bu iki metod da string tipinde veri okur. Tek fark, ReadLn, sonlandırıcı bir karakter görene kadar stringi okur, ReadString ise belirttiğiniz karakter sayısı kadar verileri okur. ReadLn‘de kullanılan sonaldırıcı karakter(Terminator) varsayılan olarak enter karakteridir. Ama isteğe göre bu karakter değiştirileiblir. ReadLn, ReadString ve ReadInteger‘ın tanımlamalarını aşağıda görebilirsiniz:



		

Eğer göndereceğiniz karakter sayısı belli değilse, daima sonlandırıcılı olan metodu tercih etmelisiniz. Çünkü diğer türlü ReadString metoduna parametre olarak girmek için daima bir karakter sayısına ihtiyacınız olacaktır. Ek olarak ReadInteger, "Network Byte Order" çevirimi de yapmaktadır. İsteğe göre bu iptal de edilebilir.

ReadStream, ReadStrings, ReadBuffer:



		


ReadStream metodu, belirlediğiniz bir stream’i okuyup bir stream üzerine almanıza yardımcı olabilir. Böylece, herhangi bir dosyayı, hafıza bölgesini ya da daha başka bir veriyi ağ üzerinden aktarabileceksiniz. Eğer AByteCount ile ne kadar veri okunacağını belirtmezseniz bu metod, bağlantı kopana kadar okumaya devam edecektir. Bu metodun kullanımını makelenin sonlarına doğru görebileceksiniz.



		


ReadStrings ise TStrings tipinde veri okur. Eğer satır sayısı olarak AReadLinesCount belirtilmezse, bir 32-bit integer ilk başta okunur ve bu değer satır sayısı olarak kullanılır.



		

ReadBuffer metodu, veriyi direk olarak belirli bir hafıza bölgesine okur. Veri, AByteCount kadar okunacaktır.

WaitFor:



		

Bu fonksiyon, belirlenen bir string okununcaya kadar tüm veriyi okur ve çıktı olarak verir. Mesela AString parametresi "FINISH" ise bu metod, "FINISH" stringini okuyuncaya kadar verileri okuyacaktır.

Okuma Zaman Aşımı

TIdTCPConnection sınıfına ait ReadTimeout özelliği milisanyie cinsinden değer alır. Yani 3 saniye için 3000 değerini girmelisiniz. Burada verdiğiniz değer, veri okuması sırasında en fazla ne kadar bekleneceğini belirler. Default olarak sıfır değerindedir. Yani herhangi bir zaman aşımı olayı gerçekleşmeyecektir. Ama bunun sonucu olarak, yazma işlemi yapan tarafta herhangi bir sorun ile karşılaşıldığında yazma işlemi gerçekleşmeyecek ve biz beklediğimiz okuma işlemini yapamayacağız. Bu nedenle Read komutumuz kanal akışını durduracaktır. ReadTimeout ile her Read komutu için ne kadar süre izin verileceği belirtilemektedir.

Yazım Metodları

Yukarıda anlattığımız okuma metodlarının aynıları yazmada da geçerlidir. Eğer Indy 9 kullanıyorsanız, bir Integer yazmak için WriteInteger, bir string yazmak için de WriteLn kullanacaksınız. Indy 10 ile beraber yazma metodlarında değişikliğe gidildi. Overload edilmiş Write metodları ile bütün bu tiplere göre ayrı ayrı metod isimlerini kullanmak yerine tek bir Write metodunu kullanıyoruz. Yani önceden:



		

olan komutlar artık:



		

şeklinde olacak.

Daha fazla yazma komutu için lütfen Indy yardım dosyalarından TIdIOHandler sınıfına göz atınız. Indy 9 için TIdTCPConnection sınıfına bakabilirsiniz. Ve gerçekten bir göz gözdirmenizde fayda var.

Yazma Tamponu(Write Buffer)

TCP, verileri paketler halinde göndermek zorundadır. Paket boyutu farklı faklıdır ama genelde 1KB civarındadır. Bununla birlikte, TCP veri paketinin tümünü beklediği halde, bazı durumlarda veri gönderilemediğinden cevaplar dönmezler. Bu yüzden TCP, paketleri ihtiyacı kadar kullanır, Nagle Coalescing algoritmasını kullanarak aynı anda paketlerin tamamına gerek duymaz. Nagle Coalescing algoritması, paket boyutuna erişene kadar veya dahildeki hesaplanmış zaman geçene kadar verileri tamponda tutar.

Küçük boyutta, çok fazla veri parçası yollanılması algoritmanın şaşırmasına sebep olur ya da çok fazla paket yollamasına sebep olur. Her paketin kendisi genel bir masraf olduğundan, bandwith’i boş yere harcar ve veri transferini yavaşlatır.

Buna çözüm olarak belki siz veriyi, bir string içinde ya da başka bir şekilde tamamını tek bir seferde yollamak isteyebilirsiniz. Tabi bu, tamponlama esnasında tüm yazma metodlarını uygun kılmayacağı gibi, kodunuzda fazladan gereksinimler olacaktır. Bu da işi daha karmaşık hale getirmekle birlikte, hafıza kullanımını da artıracaktır.

Bunun yerine, Indy’nin tampon yazım özelliğini kullanabilirsiniz. Tampon yazmayı kullanarak, giden verilerin Indy tarafından tamponlanmasına izin veriyorsunuz ve bütün yazma metodlarını normal olarak kullanabiliyorsunuz.

Tampon yazımına başlamak için, OpenWriteBuffer metodunu çağırıyorsunuz ve buffer(tampon) boyutunu belirtiyorsunuz. Bu boyuta ulaşana kadar, normal şekilde yazma fonksiyon ve metodlarınızı kullanıyorsunuz. Her buffer dolumunda ototmatik olarak tamponalnmış olan veriyi yollar ve kendisini temizler. Yani siz, tampon dolmuş mu dolmamışmı diye endişelenmenize gerek yok. Eğer herhangi bir tampon boyutu belirtmezseniz, siz elle buffer’ı gönderene kadar tüm veriler tampona alınacaktır.

Tabi ki, elle tamponu yönetmek için bir çok tampon yazım metodu da mevcuttur.

ClearWriteBuffer, yazma tamponunu temizler ama tampon hala açık kalır. FlushWriteBuffer, mevcut içeriği bağlantıya aktarır yani gönderir ve aynı şekilde tampon açık kalır.

Tampon yazımını bitirmek için CancelWriteBuffer ya da CloseWriteBuffer metodunu kullanmalısınız. CloseWriteBuffer mevcut veriyi göndererek tamponu kapatırkeni CancelWriteBuffer mevcut veriyi göndermeden tamponu kapatacaktır.

Eğer Indy 10 kullanıyorsanız(benim gibi) yukarıdaki metodları bulamayacaksınız. Indy 10’da bu metodlar WriteBufferOpen, WriteBufferFlush, WriteBufferClear, vs.. şeklinde oldu. Tabiki bu metodlar Indy 10’da TIdIOHandler sınıfında. Yani MyClient.IOHandler.WriteBufferOpen şeklinde kullanmalısınız.

Sonuç

Bence okuma ve yazma işlemleri için bu kadar bilgi yeterli. Gelecek makalemizde Server üzerinde duracağız ve örnek bir uygulama yapmaya çalışacağız. Hazırsanız Bölüm 3‘den devam edebilirsiniz.

vesselam.

Fatih Tolga Ata © 2007

Kaynaklar:

Bölüm 1 – Giriş

Bölüm 2 – Okuma ve Yazma

Bölüm 3 – Server

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

2 Comments

  • At 2007.04.09 19:37, Mustafa KUTLU said:

    Okumakla yetişemiyorum bu aralar yazdıklarına 🙂 Ellerine sağlık Fatih. Gene çok güzel bir makale olmuş.

    • At 2007.11.03 14:03, Mustafa Yalçın said:

      Çok güzel bir makale olmuş ellerine sağlık hocam 🙂

      (Required)
      (Required, will not be published)