<?xml version="1.0" encoding="UTF-8"?> <rss
version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Diyezon &#187; kanallar</title> <atom:link href="http://www.diyezon.com/tag/kanallar/feed/" rel="self" type="application/rss+xml" /><link>http://www.diyezon.com</link> <description>programlama sanatı...</description> <lastBuildDate>Tue, 24 Jan 2012 00:13:26 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.3.1</generator> <item><title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 2</title><link>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ile-threadkanal-kullanimi-bolum-2</link> <comments>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/#comments</comments> <pubDate>Sat, 06 Oct 2007 17:44:45 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[kanallar]]></category> <category><![CDATA[kritik bölgeler]]></category> <category><![CDATA[muteks]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=61</guid> <description><![CDATA[Bu bölümde kanalları nasıl eş zamanlı olarak çalıştırabileceğinizi göreceksiniz. Bunun için kritik bölgeler ve muteksler ile tanışacaksınız. Ayrıca kanal uyumlu olmayan VCL&#8217;in, kanallar ile nasıl kullanılabileceğini de göreceksiniz. Bununla beraber kanal kullanımındaki yaşanan bazı problemlere de değinmeye çalışacağız. Özellikle veritabanlarını kanallar ile kullanmada uyulması gereken püf noktalarına da değinmeye çalışacağız. Aslında bu bölüm gerçekten uzun [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bu bölümde kanalları nasıl eş zamanlı olarak çalıştırabileceğinizi göreceksiniz. Bunun için <strong>kritik bölgeler</strong> ve <strong>muteksler</strong> ile tanışacaksınız. Ayrıca kanal uyumlu olmayan VCL&#8217;in, kanallar ile nasıl kullanılabileceğini de göreceksiniz. Bununla beraber kanal kullanımındaki yaşanan bazı problemlere de değinmeye çalışacağız. Özellikle veritabanlarını kanallar ile kullanmada uyulması gereken püf noktalarına da değinmeye çalışacağız.</p><p
align="justify">Aslında bu bölüm gerçekten uzun oldu. Normalde VCL kullanımını üçüncü bir bölüme taşımayı düşünüyordum. Ama bu konuda istekler olunca birleştrip yayınlamayı düşüdüm. İçeride sizi iki sayfalık bir makale bekliyor. Bu yüzden sayfa sonunda bitti zannedip kapatmayın!</p><p
align="justify">Hazırsanız başlayalım.</p><p><span
id="more-61"></span></p><h1>Kanallar&#8217;ı Eş Zamanlı Olarak &Ccedil;alıştırmak</h1><p
align="justify">Kanallar ile uğraşırken karşılaşılan en büyük sorunlardan bir tanesi de şüphesiz kanalları ortak bir şekilde sorunsuz olarak çalıştırmaktır. Eminim kanallar ile uğraşan birisi bütün kanallar tarafından ortak olarak kullanılan bir kaynağa erişmede problemlerle karşı karşıya gelmiştir ve kanalları bir köşeye itmesine sebep olmuştur.</p><p
align="justify">Mesela şu veritabanı problemi size tanıdık gelecektir. Diyelim ki, iki adet kanalımız var ve bunlardan birincisi veritabanındaki bir kaydı açıp değişiklik yapmaya başladı. Sonra ikinci kanal aynı kaydın belli bölümlerinde düzenlemeler yaptıktan sonra veriyi kaydetti. Ardından birinci kanal, aynı verilerdeki değişikliğini bitirdi ve kaydetti. Birinci kanal, ikinci kanal&#8217;ın değişikliklerinden habersiz olduğundan ikinci kanalın yaptığı düzenlemeler de uçtu gitti.</p><p
align="justify">Burada okuduğunuz problem, normalde kanallar ve veritabanı ile uğraşan bir çok programcının başına gelmiş bir problemdir. Bununla birlikte sadece veritabanında değil bir çok noktada özellikle bir dosyayı, bir portu, bir DLL&#8217;i veya başka bir şeyi, kanalların <strong>ortak </strong>kullanmasında hep benzer sıkıntılarla karşılaşılmıştır. Bu gibi sıkıntıların ve problemlerin çözümü kanalları eş zamanlı kullanım için <strong>kritik bölgeler</strong> tanımlamak ya da <strong>muteksleri</strong> kullanmaktır. Bu iki yöntem de makalemizin konuları arasındadır.</p><h1>Kritik Bölgeler(Critical Sections)</h1><p
align="justify">Kritik Bölgeler -<em>ya da başka yerlerde görebileceğiniz gibi kritik kesimler, bölümler, vs..</em>- yukarıda anlatığımız problemdeki, birinci kanalın kaynağı kullanması esnasında ikinci kanalın bu kaynağa erişmesini <u>englelemek</u> için kullanılır. Birinci kanal işini yapıyorken, ikinci kanal&#8217;a çalışması için bir zaman dili ayrılmaz. Böylece birinci kanal işini bitirene kadar ikinci kanal çalıştırılmaz.</p><p
align="justify"> Bunun için örnek bir uygulamada yukarıdaki anlattığımız senaryolara benzer bir problem oluşturalım ve çözümünü aramaya çalışalım.</p><p
align="justify">Diyelim ki, iki adet kanalımız var ve bu kanallar bir döngünün her adımında bir dizi işlem gerçekleştiriyor. Ve bu gerçekleştirdikleri işlemler için ortak kullandıkları global olarak tanımlanmış bir değişken olsun. Meseleyi anlatabilmek için iki kanalında yaptığı işlemlerin sonucunun aynı olmasını sağlamak istiyorum. Böylece problem daha iyi anlaşılabilecek.</p><p
align="justify">Birinci kanal bir döngünün her adımında global değişkenimize teker teker harfler ekliyor ve sonuçta değişkenimizde bir yazı oluşuyor. Aynı işlemi ikinci kanalımız da yapıyor ve sonuç olarak birinci kanal ile aynı yazıyı üretiyorlar. Yanlız ikinci kanal harfleri teker teker eklemek yerine <u>ikişer ikişer</u> ekliyor. Ekleme işlemi çok hızlı olduğundan problemin oluşması için aralara <em>Sleep </em>rutinini ekleyerek işlemi birazcık yavaşlatıyoruz. Şimdi bu mantığımızı koda dökmeye başlayalım.</p></p><p
align="justify"> Yeni bir VCL uygulaması açalım ve bir adet button ekleyelim. Aşağıdaki gibi unitin var bloğunda global bir değişken tanımlayalım:</p><pre>
<pre class="brush: delphi">var
  Form1: TForm1;
  SonucYazi: string; //Global değişkenimiz</pre></pre><p
align="justify">Ardından buttonun OnClick olayını aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  Kanal1ID, Kanal2ID: DWORD;
begin
  CreateThread(nil, 0, @Kanal1, nil, 0, Kanal1ID);
  CreateThread(nil, 0, @Kanal2, nil, 0, Kanal2ID);
end;</pre></pre><p
align="justify">Bu iki adet kanalımıza ait kanal fonksiyonlarını da aşağıdaki gibi oluşturalım:</p></p><pre>
<pre class="brush: delphi">procedure Kanal1;
procedure Kanal2;

...

implementation

...

procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
  end;
end;

procedure Kanal2;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    SonucYazi := SonucYazi + &#039;di&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;ye&#039;;
    SonucYazi := SonucYazi + &#039;zo&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;n &#039;;
  end;
end;</pre></pre><p
align="justify">Gördüğünüz gibi iki kanal fonksiyonu da aynı sonucu üretiyor. Ama sonucu üretme yolları farklı. Ayrıca iki kanal da peş peşe oluşturulup çalıştırılıyor. Forma bir adet daha button ekleyelim ve bize sonucu göstersin. Bunun için ikinci butonumuzun OnClick olayını aşağıdaki gibi düzenleyelim:</p><pre>
<pre class="brush: delphi">procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(SonucYazi);
end;</pre></pre><p
align="justify">Bu programı çalıştırdıktan sonra ve kanalları birinci düğme ile çalıştırdıktan sonra sonuç yazının &quot;diyezon diyezon diyezon &#8230;&quot; gibi bir şey olmasını isteriz. Yani bir for döngüsünün bir adımı <u>tamamlanmadan</u> <em>SonucYazi </em>isimli global değişkene <u>müdahale</u> olmasını istemeyiz. Halbuki programı çalıştırdığınızda sonucun hiç de böyle olmadığını göreceğiz. <em>Kanal1</em> işini bitirip &quot;diyezon &quot; yazmadan <em>Kanal2</em> işe karışmış ve aralarda kendi harflerini eklemiştir. Hakeza bu, <em>Kanal2</em> için de geçerlidir. Yani <em>Kanal2</em>, döngünün bir adımını bitirip istenilen sonucu veremeden <em>Kanal1</em> global değişkenimize müdahale etmiştir.</p><p
align="justify">Burada verdiğimiz örnek biraz soyut kaçabilir. &Ccedil;ünkü yaptığımız işlemden sonuç olarak elimize bir şey geçmiyor. Ama karşılaşacağınız sorunlar hep bu tarzda sorunlardır. Karşılaştığınız sorunları verdiğimiz bu örnek ile kıyaslama yapabilirsiniz. Mesela biz bu örneğimizde işlem adımlarını yavaşlatmak için <em>Sleep</em> rutinini kullandık. Normal hayatta kanallar ile uğraşırken zaten yaptığımız işlemlerin adımları <em>Sleep</em> rutinini aratmamaktadır. Mesela bir veritabanı tablosunda <em>Edit</em> ve <em>Post</em> gibi metodları kullandığımızda belli bir miktar işlemin tamamlanmasını bekleriz. Bir de eğer sizde sonuç düzgün çıkıyorsa yine söylüyorum işlemciniz, bu makaleyi yazdığım sıralardaki işlemcimden hızlı demektir. Bu durumda <em>Sleep</em> rutinine verdiğimiz değerleri çok az miktarlarda artırmayı deneyin.</p><p
align="justify">Sadede gelelim ve problemi anladı isek şöyle bir soruyu kafamıza getirelim: Bir kanal belli bir işini bitirmeden önce diğer kanalların müdahalesini nasıl <u>engelleyebiliriz</u>?</p><p
align="justify">Cevabı zaten bu bölümün başında verdik. Bunun için belli yerleri <u>kritik bölge</u> ilan edeceğiz. Bir kanalda kritik bölge ilan ettiğimiz yerdeki kodlar çalışmaya başladığı sırada, diğer kanallardaki <strong><u>aynı</u></strong> kritik bölgedeki kodlar işletilmez ve bekler. Yani <strong>aynı kritik bölgeye sahip kanallardan aynı anda sadece bir bölgedeki kodlar çalıştırılabilir</strong>. Böylece diğer kanalların istenilmeyen müdahalesi engellenmiş olur.</p><p
align="justify">Bir Kritik bölge tanımlamak için bilmemiz gereken üç dört adet Windows apisi vardır.</p><p
align="justify"><font
face="Courier New"><strong>InitializeCriticalSection</strong></font>: Bu fonksiyon kritik bölge&#8217;yi temsil eden recordun ilk değerlerini atamakla sorumludur. Yani kritik bölgeyi kullanıma hazırlamaktadır. Genelde formun <em>OnCreate</em> olayında kullanılır.</p><p
align="justify"><font
face="Courier New"><strong>DeleteCriticalSection</strong></font>: Yukarıdaki fonksiyonun tam tersi olarak, bu fonksiyon da kritik bölgeyi ortadan kaldırır. Genelde formun <em>OnDestroy</em> olayına yerleştirilir.</p><p
align="justify"><font
face="Courier New"><strong>EnterCriticalSection</strong></font>: Bu fonksiyonu çalıştırdığınız satırdan itibaren kritik bölgenin başlangıcını belirtirsiniz. Yani Delphi&#8217;deki &quot;<strong><font
face="Courier New">begin</font></strong>&quot; kelimesi gibi düşünebilirsiniz.</p><p
align="justify"><font
face="Courier New"><strong>LeaveCriticalSection</strong></font>: Bu fonksiyon ise, çalıştırıldığı satırla kritik bölgeyi sınırlandırır. Yine Delphi&#8217;deki &quot;<font
face="Courier New"><strong>end</strong></font>&quot; ifadesine benzetebiliriz.</p><p
align="justify">Kritik bölgeyi temsil eden record ise <em>TRTLCriticalSection</em> tipindedir. Normalde Windows SDK yardım dosyalarında <em>LPCRITICAL_SECTION</em> şeklinde tanımlanmıştır. Ama Delphi ekibi bir çok windows yapısını tanımlarken bunlara &quot;<strong>T</strong>&quot; ile başlayan daha iyi okunur alias isimler vermişler. Her neyse&#8230;</p><p
align="justify"> Kritik bölgenin tanımlanması gayet basittir. Bir kritik bölgeyi işletim sistemine tanıtmak için <em>InitializeCriticalSection </em>fonksiyonunu kullanıyoruz. Kritik bölge ile işimiz bittiğinde de <em>DeleteCriticalSection </em>fonksiyonunu çağırmayı unutmuyoruz. Ardından kanal fonksiyonlarımızda kritik bölgeleri belirleyip <u>başına</u> <em>EnterCriticalSection </em>ve <u>sonuna</u> <em>LeaveCriticalSection </em>sonksiyonlarını koyuyoruz.</p><p
align="justify">Örneğimize dönelim ve kritik bölgeleri eklemeye başlayalım. Bizim müdahale istemediğimiz bölge, kanal fonksiyonlarındaki döngülerin her bir <u>adımıdır</u>. Yani teker teker veya ikişer ikişer ekleme yapılan kısımlarda bir adım işini bitirmeden başka bir kanalın global değişkenimize müdahalesini engellemek istiyoruz. Kısacası kritik bölgemiz bu örnekte, döngünün her bir adımı olmalıdır.</p><p
align="justify">İlk başta global olarak bir değişken daha tanımlayalım. Bu değişken bizim kritik bölgemizi temsil eden record olsun.</p><pre>
<pre class="brush: delphi">var
  Form1: TForm1;
  SonucYazi: string;
  BirinciKritikBolge: TRTLCriticalSection; //kritik bölgemizi temsil ediyor.</pre></pre><p
align="justify">Ardından formun <em>OnCreate </em>ve <em>OnDestroy </em>olaylarını aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.FormCreate(Sender: TObject);
begin
  InitializeCriticalSection(BirinciKritikBolge);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteCriticalSection(BirinciKritikBolge);
end;</pre></pre><p
align="justify">Eğer başka kritik bölgeler de tanımlamış iseniz bunları da <em>InitializeCriticalSection </em>ve <em>DeleteCriticalSection </em>fonksiyonlarını kullanarak oluşturmalı ve silme işlemlerini gerçekleştirmelisiniz. Bizim örneğimizde bir adet kritik bölge yeterlidir.</p><p
align="justify">Şimidi kanal fonksiyonlarında bahsettiğimiz yerleri kritik bölge olarak ilan edelim.</p><pre>
<pre class="brush: delphi">procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge başlangıcı
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
    LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitişi
  end;
end;

procedure Kanal2;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    EnterCriticalSection(BirinciKritikBolge); //BirinciKritikBolge başlangıcı
    SonucYazi := SonucYazi + &#039;di&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;ye&#039;;
    SonucYazi := SonucYazi + &#039;zo&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;n &#039;;
    LeaveCriticalSection(BirinciKritikBolge); //BirinciKritikBolge bitişi
  end;
end;</pre></pre><p
align="justify">Yapacağımız başka bir işlem kalmadı. Programı çalıştırıp deneybiliriz. Sonucun &quot;diyezon diyezon diyezon &#8230;&quot; gibi düzgün olduğunu göreceğiz. &Ccedil;ünkü Kritik bölge tanımlayarak sadece bir kanalın kritik bölgesini çalıştırmış olduk. Böylece bir kanal global <em>SonucYazi </em>değişkeni ile uğraşırken diğer kanal buna müdahale edemeyecektir.</p><p
align="justify">Burada bir adet kritik bölge tanımladık. Sizler ihtiyaca göre projelerinizde bir çok kritik bölge tanımlayabilirsiniz. Ayrıca kritik bölgelerin birbirinden <u>bağımsız </u>hareket ettiğini de aklınızdan çıkarmayın. Bazen karıştırılan nokta bu kısım olabiliyor. Mesela bu örneğimizde bir adet kritik bölge mevcut ama iki farklı yapılan iş var.</p><p
align="justify">Gördüğünüz gibi kanalları eş zamanlı olarak çalıştırmak o kadar da zor değilmiş. Tek yaptığımız bell bölgeleri kritik bölge olarak ilan etmek. Şimdi yeni bir kavramla daha tanışalım.</p><h1>Muteksler(Mutex)</h1><p
align="justify">İngilizce&#8217; de, <strong>mut</strong>ual ve <strong>ex</strong>clusive kelimelerinin birleşiminden meydana gelmiştir. Dilimizde ise müşterek ve seçkin kelimeleri bu kelimeleri karşılamaktadır. Programlama terminolojisinde ise, sadece tek işlemdeki değil <strong>birden fazla</strong> işlemdeki kanalları eş zamanlı olarak kullanılmasını sağlayan bir tekniktir.</p><div
align="justify">Muteksler, çalışma mantığı olarak kritik bölgelere benzerler fakat fazladan bir özellik olarak sadece aynı işlem yani sadece aynı uygulamada değil <strong>başka </strong>uygulamadaki kanallar ile eş zamanlı olarak çalışmayı da sağlarlar.</div><p
align="justify"><strong>Bir mutekse aynı anda sadece bir tek kanal sahip olabilir. </strong>Literatürde, bu mutekse, bir kanal sahip olduğunda, muteksin kanal tarafından &quot;<strong>tetiklendiği</strong>&quot; ya da &quot;<strong>tutulduğu</strong>&quot; söylenir. Aynı şekilde eğer kanal bu mutekse sahip deiğilse muteks &quot;<strong>serbestir</strong>&quot; ya da &quot;<strong>tetiklenmemiştir</strong>&quot; tabirleri kullanılır.</p><p
align="justify">Şu an kafanızda mutekslerin ne olduğuna dair somut bir düşünce oluşmamış olabilir. Ama bölümün sonuna kadar okuduğunuzda neyin ne olduğuna dair bir fikir oluşacağına eminim.</p><p
align="justify">Bir muteks, <em>CreateMutex </em>fonksiyonu ile oluşturulur. Bu fonksiyonun tanımlanması ve açıklaması aşağıdadır.</p><pre>
<pre class="brush: delphi">function CreateMutex(lpMutexAttributes: PSecurityAttributes;
  bInitialOwner: BOOL;
  lpName: PChar): THandle;</pre></pre><p
align="justify"><font
face="Courier New"><strong>lpMutexAttributes</strong></font>: Muteksin güvenliği ile ilgli parametredir. Bu parametreye <strong>nil </strong>girerek varsayılan güvenlik özelliklerini ayarlamış oluyoruz.</p><p
align="justify"><font
face="Courier New"><strong>bInitialOwner</strong></font>: Kanalın bu mutekse sahip olup olmadığını ya da yukarıda bahsettiğimiz gibi muteksin tetiklenip tetiklenmeyeceğini belirler. True ya da False giriyoruz. Genelde muteksi programın ana kanalında oluşturduğumuzdan, ana kanalın mutekse sahip olmasını istemeyiz. Bu yüzden genelde bu parametreye False gireriz.</p><p
align="justify"><font
face="Courier New"><strong>lpName</strong></font>: Eğer farklı işlemler bu mutekse erişecekse bu muteksimize bir isim vermemiz gerekmektedir. Buraya <strong>nil</strong> de girebilirisiniz. Bu durumda isimsiz bir muteksimiz olur.</p><p
align="justify">Bu fonksiyon eğer muteksi oluşturabilirse çıktı olarak muteksin handle numarasını dönderir. Eğer muteks oluşmamış ise <strong>0</strong> değerini çıktı olarak verir.</p><p
align="justify">Muteksleri, kritik bölgeler gibi düşünebilirsiniz. Kritik bölge oluşturmak için kullandığımız <em>InitializeCriticalSection</em> fonksiyonu yerine <strong>CreateMutex</strong> fonksiyonunu, kritik bölgenin başlangıcını belirten <em>EnterCriticalSection</em> yerine <strong>WaitForSingleObject</strong> fonksiyonunu, kritik bölgenin bitişini bildiren <em>LeaveCriticalSection</em> fonksiyonu yerine de <strong>ReleaseMutex</strong> fonksiyonunu kullanıyoruz. <strong>CloseHandle </strong>ile de CreateMutex ile oluşan muteksin handle&#8217;ını kapatmayı da unutmuyoruz.</p></p><p
align="justify">İlk önce bir muteks oluşturalım.</p><pre>
<pre class="brush: delphi">birmuteks := CreateMutex(nil, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">Ardından kritik bölgeyi oluşturduğumuz yerlerdeki fonksiyonları muteksin fonksiyonları ile yer değiştiriyoruz. Şimdilik sadece <em>Kanal1</em> için örnek verelim.</p><pre>
<pre class="brush: delphi">procedure Kanal1;
var
  i: Integer;
begin
  for i := 0 to 10 do
  begin
    WaitForSingleObject(birmuteks, INFINITE); //Muteksin tetiklenmesini bekle
    SonucYazi := SonucYazi + &#039;d&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;i&#039;;
    SonucYazi := SonucYazi + &#039;y&#039;;
    SonucYazi := SonucYazi + &#039;e&#039;;
    Sleep(10);
    SonucYazi := SonucYazi + &#039;z&#039;;
    SonucYazi := SonucYazi + &#039;o&#039;;
    SonucYazi := SonucYazi + &#039;n &#039;;
    Sleep(10);
    ReleaseMutex(birmuteks); //Muteksi serbest bırak.
  end;
end;</pre></pre><p
align="justify">Şimdi muteksin ne olduğuna dair kafanızda bir şeyler şekillenmeye başlmamıştır. &Ccedil;ünkü kodlara baktığımızda <em>WaitForSingleObject </em>fonksiyonu  bir şeyleri bekliyor ve <em>ReleaseMutex </em>ise bir şeyi serbest bırakıyor. <em>WaitForSingleObject</em>, &quot;<strong>tek bir nesne için bekle</strong>&quot; gibi bir manası var. Esas yaptığı görev, birinci parametresinde verilen handle numarasına göre bir muteksi <u>beklemektir</u>. İkinci parametresinde ise ne kadar bekleyeceğini belirliyoruz. Bu örneğimizde <em>INFINITE </em>(sonsuz) girmekle, bir muteks tetiklenene kadar bekleyeceğini belirttik. Bu parametreye, en fazla ne kadar bekleneceğini milisaniye şeklinde girebilirsiniz.</p><p
align="justify">Muteksi anlamada genelde verilen örnek ve bence en iyi örnek bayrak yarışıdır. Bayrak yarışlarında <u>bir şeritte ancak bir</u> koşucu koşabilir. <u>Bayrağı alan koşmaya başlar</u>. Bir şeritte, bayrağa sahip <u>olmayan</u>, koşabilmek için bayrağı <u>beklemesi</u> gerekmektedir. Böyelece her bir şeritte sadece bir yarışçı koşabilir.</p><p
align="justify">Aynen bunun gibi, kanallar çalışabilmek için mutekse sahip olmaları gerekmektedir. <u>Mutekse sahip olan koşmaya yani çalışmaya başlar</u>. Mutekse sahip olmayan, muteksin kendisine gelmesini <u>bekler</u>. Bir hatırlatma olarak, önceki paragraflarda sahip olmanın tetiklemek olduğundan bahsetmiştik.</p><p
align="justify">İşte <em>WaitForSingleObject </em>ile muteksin bu kanalımıza gelmesini beklemiş oluyoruz. Ve bunu örneğimizde, <em>INFINTE </em>yani sonsuz süre kadar bekliyoruz. Ve muteksle işimiz bittiğinde diğer kanalların kullanabilmesi için muteksi <em>ReleaseMutex </em>ile serbest bırakıyoruz. Ve yine tekrar ediyorum &quot;<strong>Bir mutekse aynı anda sadece bir tek kanal sahip olabilir.</strong>&quot;</p><p
align="justify"><em>CreateMutex </em>ile oluşturduğumuz muteksin handle&#8217;nı formun <em>OnDestroy </em>olayında <em>CloseHandle </em>ile kapatmayı da unutmuyoruz. Yani kritik bölgelerde yaptığımız <em>DeleteCriticalSection </em>yerine</p><pre>
<pre class="brush: delphi">CloseHandle(birmuteks);</pre></pre><p
align="justify">gibi bir şey yazmalıyız.</p><p
align="justify">Bu örneği, makaleyi fazla uzatmasın diye, burada tüm kodlarını yazmayacağım. Muteks örnek projesini <a
href="http://www.diyezon.com/wp-content/uploads/File/post61/muteks1.rar">buradan</a> indirebilirsiniz.</p><p
align="justify">Eğer tek bir muteks değil de bir den fazla muteksi tetiklemesini bekelemek istersek ne olacak? Bu durumda <strong>WaiıtForMultipleObjects </strong>fonksiyonunu kullanacağız. <em>WaitForSingleObject </em>tek bir muteksi beklerken <em>WaitForMultipleObject </em>fonksiyonu birden fazla muteksi beklemektedir. Kullanım aynı olduğu için bununla ilgili bir örnek yapmayacağım. Bu fonksiyon ile iligli tek söyleyebileceğim şey ikinci parametrede beklediğiniz mutekslerin handle&#8217;larını dizi şeklinde girmeniz olacaktır.</p><h2>Başka İşlemlerden Mutekslere Sahip Olma</h2><p
align="justify">Dediğimiz gibi, mantık olarak mutekslerin kritik bölgelerden tek farkı, başka program ve işlemlerdeki kanallar ile ortak ve eş zamanlı çalışmaya izin vermesidir. Bunun için muteksimize bir isim vermemiz gerektiğini söylemiştik. İşte bu ismi kullanarak başka bir işlem muteksin handle numarasını alabilir. Gerisinde de <em>WaitForSingleObject </em>ve <em>ReleaseMutex </em>fonksiyonlarında bu handle numarasını kullanabiliriz. Yani mevcut bir muteksin handle numarasını alabilmek için:</p><pre>
<pre class="brush: delphi">birmuteks := OpenMutex(0, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">dememiz yeterli. Bunu yukarıdaki örneğimizde <em>CreateMutex </em>yerine kullanabilirsiniz. Ama unutmamanınız gereken nokta bunun ile <strong>mevcut </strong>bir muteksin handle numarasını alırız. Eğer muteks <em>CreateMutex </em>ile hiç oluşturulmamış ise bu fonksiyon <strong>0</strong> değerini dönderir.</p><p
align="justify"><em>OpenMutex </em>fonksiyonunun parametreleri <em>CreateMutex </em>ile aynı olduğundan burada fazladan bir açıklama yapmayacağım.</p><p
align="justify">Bu konuda bir örnek yapmak isterseniz yukarıda verdiğimiz örneği iki uygulamaya bölebilirsiniz. <em>Kanal1</em>&#8216;i birinci uygulamada, <em>Kanal2</em>&#8216;yi de ikinci uygulamada oluşturursunuz. Ve ikisinde de <em>OnCreate</em> olayında şöyle bir şey girersiniz:</p><pre>
<pre class="brush: delphi">var
  BirMuteks: THandle;
.....
procedure TForm1.OnCreate(....);
begin
  BirMuteks := OpenMutex(0, False, &#039;ornekmuteks&#039;);
  if BirMuteks = 0 then
    BirMuteks := CreateMutex(nil, False, &#039;ornekmuteks&#039;);</pre></pre><p
align="justify">Kısaca burada yaptığımız işlem eğer &quot;ornekmuteks&quot; isminde bir mutex henüz oluşturulmamış ise oluşturuyoruz.</p><p
align="justify">Bu şekilde iki veya daha fazla sayıda işlem tek bir muteks üzerinde kanallarını eş zamanlı olarak çalıştırabilirler. Bu örneği kendiniz yapmaya çalışın. Gerçi <a
href="http://www.diyezon.com/wp-content/uploads/File/post61/muteks2.rar">burada</a> yapılmışı var <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Ama lütfen indirip incelemeden önce kendiniz yapmayı deneyin.</p><p>&#8212;</p></p><p
align="justify">İşte size iki adet yöntem. Ve ikisi de windows programlamada çok sık kullanılmaktadır. Kanalları beraber çalıştırmak istediğinizde, istrer muteks kullanın isterseniz kritik bölge tanımlayın. Her ikisinin de tanımlanması ve kullanılması çok kolay. Kafanızdaki &quot;kanal kullanmak çok zordur&quot; düşüncesini birinci bölümde aştığınızı düşünüyorum. Buraya kadar olan kısım ile de &quot;kanalları eş zamanlı çalıştırmak zordur&quot; düşüncesini yendiğinizi zannediyorum.</p><p
align="justify">Peki muteks ve kritik bölge arasında tercih yapmak gerekirse hangisini tercih etmeliyiz. Bence performans açısından pek fark olmaz. Seçim noktanız şu olmalı. Eğer, tek bir işlemden değil de bir çok işlemden yani bir çok programdan oluşan bir gurubun kanalları ortak ve eş zamanlı kullanmasını düşünüyorsak kesinlikle muteks kullanmalıyız. Aksi durumda yani eş zamanlı çalışacak olan kanallar sadece tek bir işlemde bulunuyorlarsa o zaman seçim size kalmış. Hangisi rahatınıza gidiyorsa onu kullanabilirsiniz.</p><p
align="justify">Şimdi farklı bir konuya geçiş yapalım. Hazırsanız ikinci sayfadan devem edelim.</p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-2/feed/</wfw:commentRss> <slash:comments>19</slash:comments> </item> <item><title>Delphi ile Thread(Kanal) Kullanımı &#8211; Bölüm 1</title><link>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=delphi-ile-threadkanal-kullanimi-bolum-1</link> <comments>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/#comments</comments> <pubDate>Tue, 02 Oct 2007 14:27:48 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[kanallar]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=58</guid> <description><![CDATA[Yaptığımız projelerin bir bölümü eninde sonunda bu konuya dayanıyor. Genelde kanal gerekli olduğu halde Timer ya da Application.ProcessMessages gibi çarelere gitmeye çalışıyoruz Ve devamında hem görsel hem de işleyiş açısından istemediğimiz sonuçlarla karşılaşıyoruz. Halbuki kanalların kullanılması sanıldığı kadar zor değildir. Bu ön yargıyı bu makalede aşmaya çalışacağız. Bu makale, ileri seviye Windows programcılığına geçiş kapısıdır. [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Yaptığımız projelerin bir bölümü eninde sonunda bu konuya dayanıyor. Genelde kanal gerekli olduğu halde Timer ya da Application.ProcessMessages gibi çarelere gitmeye çalışıyoruz Ve devamında hem görsel hem de işleyiş açısından istemediğimiz sonuçlarla karşılaşıyoruz. Halbuki kanalların kullanılması sanıldığı kadar zor değildir. Bu ön yargıyı bu makalede aşmaya çalışacağız.</p><p
align="justify">Bu makale, ileri seviye Windows programcılığına geçiş kapısıdır. Bu makeledeki konuları öğrendikten sonra Win32 programcılığının en güçlü özelliklerini öğrenmiş olacaksınız.</p><p
align="justify">Yanlız &quot;ileri seviye&quot; tabiri gözünüzü korkutmasın. &Ccedil;ünkü makeleyi bitirdiğinizde thread kullanımının aslında ne kadar kolay olduğunu göreceksiniz. Bununla birlikte bu makalede bu konu ile ilgili her ayrıntıyı anlatabilmemiz için yüzlerce sayfalık kitap yazmamız gereklidir. Bu yüzden burada işin mantığını kapıp, ihtiyacınız oldukça yardım dosyalarına müracaat etmeniz gerekebilir.</p><p
align="justify">Kanallar hakkında hiç bir bilginiz olmayabilir. Bu makalede işin temelinden alacağız ve makaleyi bitirdiğinizde kendi programlarınıza kanal ekleyebilecek duruma geleceğinize inanıyorum.</p><p
align="justify">Bu makale serisinde kanallar temelden anlatılmakla beraber, ileri seviye kanal kullanımı, VCL bileşenlerinde kanal kullanımı ve Muteksler de ana konularımız arasına girecektir.</p><p><span
id="more-58"></span></p><h1>Giriş</h1><p
align="justify">İlk başta bir kaç teorik bilgi ile başlamak istiyorum. Bu bilgilieri olabildiğince sıkmadan, kısa tutmaya çalışacağım.</p><p
align="justify">Disk üzerindeki her bir dosya çalıştırıldığında artık windows için birer <strong>işlem</strong>(<em>process</em>) olurlar. Bir <strong>işlem </strong>Windows için fazla bir şey ifade etmez. &Ccedil;ünkü işlemler sadece hafızada belli bir bölgede var olmaktan sorumludur. Esas işlemi yapan kısım <strong>kanallardır</strong>(<em>thread</em>). Bir işlem en az bir adet kanala sahiptir. Win 3.1 gibi işletim sistemleri sadece bir adet kanala sahiptir. Ama Windows 95 ve üstü, Unix, OSX gibi işletim sistemleri birden fazla kanala sahip olabilirler.</p><p
align="justify">İşletim sistemi bir programı ya da bir DLL&#8217;i ilk başta işlem olarak hafızaya taşır. Bu esnada işlem eylemsiz olarak durur. Bu işleme ait kanallar ise bizim belirlediğimiz ölçüde programın kodlarını çalıştırmaya başlar.</p><div
align="justify"> Yine teorik olarak bilmemiz gereken diğer bir terim de quantum değeridir. Her kanalın windows tarafından atanmış bir işletilme süresi vardır. Ve terim olarak quantum olarak isimlendirilir. Bunun sadece tanımını bilmeniz yeterli.</div><p
align="justify">Her kanal, çalışma esnasında kendi eax, ebx, edx gibi register verilerini tutan bir yapıya sahiptir. Bu yapıya <strong>context</strong>(<em>içerik</em>) adı verilmektedir. Bu yapının Delphi&#8217;deki karşılığı <em>TContext </em>olup, Windows unitindedir. Bu record tipini incelediğinizde bir çok register&#8217;ı barındırdığını göreceksiniz. Eğer <a
href="http://www.diyezon.com/?p=45">buradaki</a> Delphi ve Assembler ile ilgili makaleyi ve <a
href="http://www.diyezon.com/?p=47">buradaki</a> fonksiyon çağırım makenizmalarını okudu iseniz şimdi anlatacaklarımız size tanıdık gelecektir. &Ccedil;ünkü kanalların işleyiş mantığı fonksiyon çağırımlarına benzemektedir. Ama devam edebilmek için bunları okumak zorunda değilsiniz.</p><p
align="justify">Her bir kanal sahip olduğu önceliğe göre işletim sistemi tarafından işleme alınacaktır. Bu işleme alış esnasında sahip olduğu <em>context</em>&#8216;deki register değerleri yüklenir. Esp, Ebp, Eip gibi registerlar da yüklendiğinden, kanal önceden kaldığı yerden önceki stack değerlerine göre işlemine devam edecektir. Ve yine önceliğine göre belli bir süre sonunda işletim sistemi bu kanalı durdurup register değerlerini <em>context </em>yapısında saklar. Daha sonra diğer bir kanalın context verisini yükleyerek başka bir kanalı işlemeye geçer. Ve hakeza bu işlem tüm kanallar için devam edip gider. İşletilme ve geçiş süreleri çok kısa olduğundan, biz sanki aynı anda tüm kanallar işletiliyormuş gibi zannederiz. Halbuki bütün kanallar belli bir sıra ile çok kısa sürelerde işletilip diğer bir kanala geçilmektedir. Yani her kanal aynı anda çalışmaz, biz öyle zannederiz. Tabi bu anlattıklarımız tek işlemci ve tek çekirdek için geçerlidir. Kanalların işletilme sürelerini ise kanalların <strong>önceliği </strong>belirlemektedir. Hangi kanal daha yüksek öncelikli ise o kanal daha fazla işletilme süresine sahip olur. Eğer kanal arkaplanda çalışma üzere ayarlanmış ise, diğer kanallar işlemlerini tamamladıktan sonra ancak bu kanala sıra gelecektir. Eğer kanal çok yüksek önceliğe sahip ise uzun bir süre bu kanal işleme devam edecek ve diğer kanallara sıra gelmeyecektir.</p><p
align="justify">Demek ki, aynı anda bir kaç işlem yapmak istiyorsak kafa patlatıp, parmak yormamıza gerek yokmuş. &Ccedil;ünkü işletim sistemi zaten sizin için bu sistemi kurmuştur. Sadece biraz vaktiniz ayırmanız ve kanallar nasıl oluşturuluyor öğrenmeniz yeterlidir.</p></p><h1>Kanal Oluşturma Yöntemleri</h1><p
align="justify">Aslında böyle bir başlıkla konuya giriş yapmak istemezdim. Ama kanal oluşturmayı öğrenmeden önce bir kaç şeyi açığa kavuşturmak istiyorum. Gerek forumlardan gerekse haber guruplarından kanallarla ilgili gelen sorulardan bir çoğu kanalların yanlış oluşturulması üzerine kaynaklanan yanlışlıklardan gelmektedir.</p><p
align="justify">Normalde bir kanal windows ortamında <em>CreateThread</em> Windows API&#8217;si ile oluşturulur. Fakat VCL bize <em>TThread </em>isminde bir sınıf da sunmuştur. Kullanım açısından ayırt edebileceğiniz temel nokta şudur. Eğer kanalda yapacağınız işlemler VCL sınıflarını etkilemesi gerekiyorsa <em>TThread </em>sınıfını kanal oluşturmak için kullanıyoruz. Yok eğer VCL sınıflarını ilgilendiren bir işlem söz konusu değilse <em>CreateThread</em> API&#8217;sini kullanıyoruz. Bunun neden böyle olduğunu ileride VCL ve Kanal kullanımı ile alakalı başlıkda vereceğim. Muhtemelen Bölüm 2&#8242;de göreceksiniz. Bu yüzden biraz sabır.</p><p
align="justify">Başlarken bu şekilde bir giriş yapmamın sebibi buradan kaynaklanıyor. Bir çok kişi belki de Windows API&#8217;lerine karşı bir soğukluktan dolayı <em>CreateThread </em>ile uğraşmak istemiyor ve <em>CreateThread</em> ile ilgili kısımları gözü kapalı atlayabiliyor. Bu yüzden eksik kanal bilgisi ile ileride bir çok sorunla karşılaşabiliyor. Ama şimdi göreceğimiz gibi kanal oluşturma işlemi sanıldığı kadar zor ve karmaşık bir işlem değil.</p><h1>Bir Kanal Oluşturalım</h1><p
align="justify">Kanal oluşturma ile sorumlu Windows fonksiyonu <em>CreateThread </em>fonksiyonudur ve aşağıdaki gibi tanımlanmıştır:</p><pre>
<pre class="brush: delphi">
function CreateThread(lpThreadAttributes: Pointer;
  dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine;
  lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
</pre></pre><p
align="justify"> Bu fonksiyonun parametrelerinin kısaca açıklamasını verelim.</p><p
align="justify"><font
face="Courier New"><strong>lpThreadAttributes</strong></font>: Kanalımıza atanacak olan bir çok güvenlik özelliğini belirtmemize yarar. Eğer nil girerseniz varsayılan güvenlik özellikleri kullanılacaktır. Win9x ve WinMe için bu değer <font
face="Courier New"><strong>nil</strong></font> olmalıdır. Bu konu ile alakalı daha fazla bilgi almak için sdk yardım dosyalarından <em>SECURITY_ATTRIBUTES</em> başlığına müracaat edebilirsiniz.</p><p
align="justify"><font
face="Courier New"><strong>dwStackSize</strong></font>: Eğer kanal için özel bir stack büyüklüğü belirlemeyecekseniz bu değeri <strong>0</strong> olarak girmelisiniz. Böylelikle kanalın stack büyüklüğü program ile aynı olacaktır. Gerektiğinde stack boyutu otomatik olarak genişletilebilir.</p><p
align="justify"><font
face="Courier New"><strong>lpStartAddress</strong></font>: Bu parametre esas işi gören parametredir. &Ccedil;ünkü kanal çalışmaya bu parametredeki fonksiyon işaretçisi ile beraber başlar. Burada tek yapmamız gereken fonksiyonun isminin önüne <strong>@</strong> işareti vererek girmektir.</p><p
align="justify"><font
face="Courier New"><strong>lpParameter</strong></font>: Bir önceki parametrede girdiğimiz kanal fonksiyonuna parametre yollamak istiyorsanız, bu parametrelerin içinde bulunduğu bir record&#8217;un adresini buraya girmelisiniz. Ayrıca record haricinde karakter veya sayı değelerini de parametre olarak yollayabiliriz.</p><p
align="justify"><font
face="Courier New"><strong>dwCreationFlags</strong></font>: Bu parametre ile kanalın oluşturulur oluşturulmaz çalışıp çalışmayacağını belirliyoruz. Eğer bu parametreye <em>CREATE_SUSPENDED</em> girerseniz, kanal oluşturulur fakat kendisine çalışmaya başlaması için izin verilmez. Bu durumda siz <em>ResumeThread</em> fonksiyonu ile bu kanala çalışma talimatı vermedikçe kanal o şekilde çalışmadan duracaktır. Bir kez <em>ResumeThread</em> ile çalışmaya başladıktan sonra <em>SuspendThread</em> ile tekrar kanalı durdurabilirsiniz. Eğer bu parametreye <strong>0</strong> girerseniz, kanal oluşturulur oluşturulmaz çalışmak için sıraya girecektir.</p><p
align="justify"><font
face="Courier New"><strong>lpThreadId</strong></font>: Bu parametreye kanal kimlik ID&#8217;si atanır. Buraya değeri olmayan bir Dword değişken giriyoruz. Değişken gireceğiz çünkü &quot;<strong>var</strong>&quot; olarak tanımlanmış. &Ccedil;ünkü kanal oluşturulduktan sonra Windows NT,2000 ve üstü işletim sistemlerinde kanalın ID&#8217;si bu değişkene yüklenir. Windows 9x/Me işletim sistemlerinde bu değer daima 0&#8242;dır.</p><p
align="justify">Şimdi gelin bu fonksiyonu kullanarak bir kanal oluşturalım.</p><pre>
<pre class="brush: delphi">var
  KanalID: DWORD;
begin
  CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KanalID);</pre></pre><p
align="justify">İşte hepsi bu! Tek yaptığımız sadece gerekli parametreleri kullanmak, gerisini duruma göre <strong>nil</strong> veya <strong>0</strong> yapmak. Bu fonskiyon çağrıldığında <em>KanalFonksiyonu </em>isimli fonksiyonumuz kanal içinde çalışmaya başlayacaktır.</p><p
align="justify">Dilerseniz <em>KanalFonksiyonu </em>isimli fonksiyonumuzun tanımlamasını şimdi yapacağımız örnek içinde verelim.</p><h2> Basit Bir Örnek</h2><p
align="justify">Şimdi oluşturduğumuz bu kanalın nasıl çalıştığını basit bir örnek ile görmeye çalışalım. Yeni bir uygulama oluşturalım ve Form üzerine bir adet button ve bir adet label koyalım. Button&#8217;nun <em>Caption </em>özelliğini &quot;Oluştur ve &Ccedil;alıştır&quot; olarak değiştirelim. Ve Button&#8217;nun <em>OnClick </em>olayına kanalımızı oluşturan şu kodları girelim:</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  KimlikID: DWORD;
begin
  CreateThread(nil, 0, @KanalFonksiyonu, nil, 0, KimlikID);
end;</pre></pre><p
align="justify">Şimdi de kanalımızda esas işi yapan <em>KanalFonksiyonu </em>isimli fonksiyonumuzun tanımlamasını girelim:</p><pre>
<pre class="brush: delphi">function KanalFonksiyonu(P: Pointer): Longint; stdcall;

...

implementation

...

function KanalFonksiyonu(P: Pointer): Longint; stdcall;
var
  i: Integer;
  Toplam: Int64;
begin
  Toplam := 0;
  for i := 0 to 1000000000 do
  begin
    Toplam := Toplam + i;
    Toplam := Toplam shl 4;
  end;
  Form1.Label1.Caption := &#039;Kanal işlemini tamamladı. Sonuç:&#039; + IntToStr(Toplam);
end;</pre></pre><div
align="justify"><p>Kanal fonksiyonumuzun tanımlamasını istediğimiz gibi yapabilirdik. Burada mesela <font
face="Courier New">P: Pointer</font> parametresine ihtiyaç yoktu. Ayrıca <em>Longint </em>çıktısını vermeye de ihtiyacımız yok. <em>CreateThread</em> fonksiyonunun parametre açıklamalarını hatırlarsanız, kanal fonksiyonumuza belli parametreler yollayabiliyorduk. İşte bu parametreleri pointer olarak buradan alıyoruz. Ama bu meseleye sonra geçeceğiz. Ayrıca fonksiyon tanımlamasındaki <em>stdcall </em>ifadesine yabancı iseniz <a
href="http://www.diyezon.com/?p=47">buradaki </a>fonksiyon çağırım makenizmaları ile ilgili makaleyi okuyabilirsiniz.</p></div><p
align="justify">Bu fonksiyonda ekstra bir şey yapmıyoruz. Sadece bir for döngüsü var ve matematiksel bir işlem yapılıyor. For döngüsü bittiğinde ise formdaki label&#8217;a bir sonuç yazdırıyoruz.</p><p
align="justify">Bu for döngüsünü, ayrı bir kanalda değil de programın ana kanalında çalıştırmaya kalksaydık muhtemelen <em>Application.ProcessMessages</em> ve <em>TTimer</em> gibi çözümlere gidecektik. Bu durum da çok fazla miktarda performans düşüklüğüne sebep olacaktı. Ayrıca işlemlerimiz genelde buradaki gibi basit matematiksel işlemler olmadığından, programda çökmelere ve donmalara sebep olacaktık.</p><p
align="justify">Bu programı çalıştıralım ama button&#8217;a basmayalım. Eğer açık değilse View menüsünden &quot;Thread Status&quot; penceresini ve &quot;Event Log&quot; penceresini açalım ve görebileceğimiz bir köşeye yerleştirelim. Eğer BDS 2005 ve üstü kullanıyorsanız bu pencereler debug esnasında altta görünür biçimde hazır olacaklardır.</p><p
align="justify">Ardından buttona bir kez basalım ve &quot;Thread Status&quot; penceresine yeni bir eleman eklendiğine dikkat edelim. Eğer buradaki eleman çok çabuk bir şekilde görünüp kayboldu ise işlemciniz benimkinden hızlı demektir <img
src='http://www.diyezon.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Bu yüzden for döngüsündeki limit değeri biraz artırıp tekrar çalıştıralım.</p><p
align="justify">Button&#8217;a her tıkladığınızda &quot;Thread Status&quot; pencersinden de takip edebileceğiniz gibi yeni bir kanal eklenecek. Tabi butona basma işini o kadar çok abartmayın. Her kanal oluşturulduğunda kendisine bir ID atanacak. Örneğimizde bu değer <em>KimlikID</em> değişkeninde tutuluyor. &quot;Thread Status&quot; pencersinde ise ThreadID sütununda bu değeri görebiliriz. Her kanal işlemini bitirdiğinde &quot;Thread Status&quot; pencersinden kaybolduğunu göreceksiniz. Aynı şekilde &quot;Event Log&quot; pencersinde de kanal&#8217;ın ID&#8217;sine göre başlama ve bitişi ile ilgili mesajları görebilirsiniz.</p><p
align="justify">Kanal çalışırken program üzerinde istediğiniz işleme devam edebilirsiniz. Formu taşıyabilir, boyutlandırabilir, başka bileşen ve nesneler var ise onları kullanabilirsiniz. Ve bunu yaparken bir yandan kanal(lar) da çalışmaktadır. Aynı kanal fonksiyonunu doğrudan çağırmaya kalktığımızda formumuza döngü bitene kadar müdahale edemeyecektik.</p><p
align="justify">Gördüğünüz gibi bir kaç satır kod ile kanal oluşturuveriyoruz. Tek bilmemiz gereken kısım <em>CreateThread</em> fonksiyonunun parametreleri. Bunların çoğunu da kullanmadık. Ama makalenin ilerleyen kısımlarında bunları da yavaş yavaş kullanıma dahil edeceğiz. İşte bir tanesi geliyor.</p><h1> Kanal Fonksiyonlarına Parametre Gönderimi</h1><p><em>CreateThread</em> fonksiyonunun parametrelerini açıklarken <em>lpParameter</em>&#8216;den söz etmiştik ve bu parametre ile kanal fonksiyonuna istediğimiz parametreyi gönderebileceğimizi belirtmiştik. Şimdi bunu nasıl yapacağımızı görelim.</p><p>Bunun için yukarıda yaptğımız örnekten faydalanacağız. İlk başta <em>type </em>bloğunda aşağıdaki record tanımlamasını yapalım.</p><pre>
<pre class="brush: delphi">
  PKanalParametresi = ^TKanalParametresi;
  TKanalParametresi = record
    BirParametre: string;
  end;
</pre></pre><p> Ardından kanal fonksiyonumuzu aşağıdaki gibi değiştirelim.</p><pre>
<pre class="brush: delphi">function KanalFonksiyonu(P: Pointer): Longint; stdcall;
var
  Parametreler: PKanalParametresi;
begin
  Parametreler := PKanalParametresi(P); //Parametremizi alalım.
  while True do
  begin
    Form1.Label1.Caption := Form1.Label1.Caption + Parametreler^.BirParametre;
    Sleep(1000); //İşlemcinin %99 çalışmasını istemiyiz değil mi....
  end;
  Dispose(Parametreler);
end;</pre></pre><p>En son olarak button&#8217;nun <em>OnClick </em>olayını da aşağıdaki gibi olacak şekilde değiştirelim.</p><pre>
<pre class="brush: delphi">procedure TForm1.Button1Click(Sender: TObject);
var
  KimlikID: DWORD;
  Parametreler: PKanalParametresi;
begin
  New(Parametreler);
  Parametreler^.BirParametre := &#039;a&#039;;
  CreateThread(nil, 0, @KanalFonksiyonu, Parametreler, 0, KimlikID);
end;</pre></pre><p
align="justify">Aslında çok fazla bir şey yapmadık(en azından siz kopyala yapıştır yaptınız:) ). Burada basit bir işaretçi kullanımını görüyoruz. Ve parametre olarak bir record&#8217;u kullandık. Record yerine başka veri tiplerini de kullanabilirdik. Ama genelde karşılaşılan sorunlar recordları parametre olarak yollamaktan gelmektedir.</p><p
align="justify">En başta verdiğimiz record tanımlamasına bakarsanız, <em>PKanalParametresi</em> isminde bu recordumuza <u>işaretçi</u> olan bir tip göreceksiniz. Parametremizi işaretçi olarak kullanacağımızdan bu tipe ihtiyacımız olacak. Ardından Button&#8217;nun <em>OnClick</em> olayına bakalım. Burada önceki örneğe göre fazladan 2 satır ekledik. İlk başta <em>var</em> bloğunda <em>Parametreler</em> isminde bir record işaretçisi tanımladık. Ardından işaretçi hiç bir şey ifade etmediğinden, işaretçiye ait bir recordun hafızada oluşturulması için <em>New</em> rutinini kullandık. Devamında bu record&#8217;un değişkenlerinden birine bir <em>string </em>değer atadık. <em>CreateThread</em> fonksiyonu parametreyi işaretçi olarak istediğinden buraya direk olarak <em>Parametreler</em> değişkenimizi girebiliriz. &Ccedil;ünkü bu değişken bir record işaretçsidir.</p><p
align="justify">En son olarak yeni kanal fonksiyonumuza baktığımızda, işaretçi olarak gelen parametremizin nasıl kullanıldığını görüyoruz. Burada bizim için önemli olan kısım <em>Dispose</em> kısmıdır. &Ccedil;ünkü burada <em>New</em> ile oluşturduğumuz ve işaretçiye ait olan recordu hafızadan siliyoruz.</p><p
align="justify">Record işaretçisi yerine normal record kullansa idik ve sonra da bunu parametre olarak geçirirken pointer olarak geçirse idik olmazmıydı. Olurdu&#8230; &Uuml;stelik <em>New</em> ve <em>Dispose</em> rutinlerini de kullanmak zorunda kalmazdık. Ama, bu durumda boş yere hafızda yer işgal edecektik. &Ccedil;ünkü parametre recordu sadece bir kerelik işimize yarayacak. Bu yüzden işimiz bitince <em>Dispose</em> ile hafızdan siliyoruz.</p><p
align="justify">Bu örneğimizi de çalıştırıp deneyebilirsiniz. Ayrıca Görev Yöneticisi ile işlemciyi yüzde kaç kullandığını da görebilirsiniz. Tabi burada <em>Sleep</em> rutinini kullanmak yerine daha başka yapılabilecek şeyler var ama şimdilik kafa karıştırmamak için bunları sonraya bırakıyorum.</p><h1>Sonuç</h1><p
align="justify">Bu bölümü burada noktalayalım. Gelecek bölümde kanalların nasıl birbirleri ile birlikte çalışabileceğini göreceğiz. Ayrıca Mutex&#8217;ler ve VCL ile kanal kullanımı da gelecek bölümde göreceğimiz konular arasında olacak. Böylece kanallar konusunda hiç bir şüpheniz, korkunuz kalmayacağı gibi programlarınıza da profesyonellik katacaksınız.</p><p
align="justify">Gelecek bölümde buluşmak ümidi ile&#8230; Yorumlarınızı ve eleştirilerinizi bekliyorum. Yine her zaman ki gibi sorularınızı buradan yada Delphi Türkiye <a
href="http://www.delphiturkiye.com/forum">forum</a>larından iletebilirsiniz</p><p
align="justify">Fatih Tolga Ata &copy; 2007</p><h2>Kaynaklar</h2><ul><li>Windows SDK Yardım Dosyası</li><li>Delphi 4 Unleashed, Charlie Calvert, Sams Pub., 1999</li></ul> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/delphi-ile-threadkanal-kullanimi-bolum-1/feed/</wfw:commentRss> <slash:comments>22</slash:comments> </item> <item><title>Indy ile Network Programlama 3 &#8211; Server</title><link>http://www.diyezon.com/indy-ile-network-programlama-3-server/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=indy-ile-network-programlama-3-server</link> <comments>http://www.diyezon.com/indy-ile-network-programlama-3-server/#comments</comments> <pubDate>Wed, 11 Apr 2007 22:27:03 +0000</pubDate> <dc:creator>Fatih Tolga Ata</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[Indy Kütüphanesi]]></category> <category><![CDATA[Network Programlama]]></category> <category><![CDATA[client]]></category> <category><![CDATA[command handlers]]></category> <category><![CDATA[ip]]></category> <category><![CDATA[kanallar]]></category> <category><![CDATA[komut denetleyicileri]]></category> <category><![CDATA[network]]></category> <category><![CDATA[server]]></category> <category><![CDATA[server modelleri]]></category> <category><![CDATA[socket]]></category> <category><![CDATA[tcp]]></category> <category><![CDATA[TIdTCPServer]]></category> <category><![CDATA[udp]]></category><guid
isPermaLink="false">http://www.diyezon.com/?p=37</guid> <description><![CDATA[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&#8217;ı nasıl tanımlayabileceğimize bakacağız. Server oluşturma modellerini incelemeye çalışacağımız gibi bir server oluştururken takip edilen yolları [...]]]></description> <content:encoded><![CDATA[<p
align="justify">Bundan önce, gerek client tarafında gerekse server tarafında okuma ve yazma işlemlerinin nasıl yapıldığını <a
href="http://www.diyezon.com/?p=36">gördük</a>. 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&#8217;ı 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.</p><p><span
id="more-37"></span></p><p><a
href="http://www.diyezon.com/?p=35">Bölüm 1 &#8211; Giriş</a></p><p><a
href="http://www.diyezon.com/?p=36">Bölüm 2 &#8211; Okuma ve Yazma</a></p><p><a
href="http://www.diyezon.com/?p=37">Bölüm 3 &#8211; Server</a></p><h1>TIdTCPServer</h1></p><p
align="justify">En önemli Indy server, <font
face="Courier New">TIdTCPServer</font> bileşenidir. <font
face="Courier New">TIdTCPServer</font>, programın ana kanalından bağımsız olan ikincil bir dinleme kanalı oluşturur. Dinleme kanalı, client&#8217;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.</p></p><p><img
width="505" vspace="5" hspace="5" height="160" border="0" align="middle" src="/wp-content/uploads/Image/post37/Indy3_1.png" alt="Å?ekil1: Indy TCP Server Kanal Yönetimi" /></p><h1>Kanalların Rolleri</h1><p
align="justify">Indy server&#8217;ları, kanallar etrafında dizayn edilmiştir ve Unix server&#8217;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.</p><p
align="justify">Genelde Unix server&#8217;ları, client&#8217;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.</p><p
align="justify">Indy serverları da benzer biçimde işlemleri gerçekleştirir. Unix&#8217;in aksine, Windows fork&#8217;lar ile iş görmez. Bunun yerine benzer ama aynı olmayan kanalları işler. Unix&#8217;in bir iş ayırmasının aksine Indy server&#8217;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.</p><p><strong>Yüzlerce Kanal</strong></p><p
align="justify">&Ccedil;ok meşgul bir server&#8217;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.</p><p
align="justify">Å?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.</p><p
align="justify">&Ccedil;oğu server&#8217;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&#8242;ünün de aynı anda çalıştığı manasına gelmez.</p><p
align="justify">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.</p><p><strong>Gerçekçi Kanal Limitleri</strong></p><p
align="justify">Bir işlemin 1000&#8242;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.</p><p
align="justify">&Ccedil;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&#8217;yi kullanan serverlar böyle binlerce kanal oluşturan tiplerdendir.</p><p
align="justify">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&#8217;a izin verir.</p><p
align="justify">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.</p><h1>Server Modelleri</h1><p
align="justify">TCP Server oluşturabilmek için iki yol mevcuttur. <strong>Komut Denetleyicileri</strong>(<em>Command Handlers</em>) ve <strong>OnExecute</strong> olayı. Komut denetleyicileri server oluşturmayı çok kolaylaştırmaktadır, ama her duruma uygun değildir.</p><p
align="justify">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. &Ccedil;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.</p><p><strong>OnExecute Olayı</strong></p><p
align="justify">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 <font
face="Courier New">OnExecute</font> olayı kullanılmalıdır. <font
face="Courier New">OnExecute</font>, 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.</p><pre>
<pre class="brush: delphi">procedure TForm1.MyTCPServerExecute(AContext: TIdContext);
var
  Command: string;
begin
  with AContext.Connection.IOHandler do
  begin
    command := Trim(ReadLn);
    if command = &#039;CIKIS&#039; then
    begin
      WriteLn(&#039;200 Good Bye&#039;);
      Disconnect;
    end
    else if command = &#039;SAATIN_KAC&#039; then
    begin
      WriteLn(&#039;200 &#039; + TimeToStr(Time));
    end
    else
    begin
      WriteLn(&#039;400 Bilinmeyen komut&#039;);
    end;
  end;
end;</pre></pre><p
align="justify">Indy 10&#8242;dan önceki versiyonlarda, <font
face="Courier New">OnExecute</font> olayının parametresi <font
face="Courier New">AContext</font> değil <font
face="Courier New">AThread</font>&#8216;dir. Ve <font
face="Courier New">WriteLn</font>, <font
face="Courier New">ReadLn</font> gibi komutlar, önceki makaleden de hatırlayacağınız gibi, <font
face="Courier New">IOHandler</font> içinde değil direk olarak <font
face="Courier New">TIdConnection</font> içindedir. Yani Indy 10 için yazdığımız yukarıdaki with bloğunu şöyle yazmalıyız:</p><pre>
<pre class="brush: delphi">with AThread.Connection do</pre></pre><p
align="justify">Uygun bir bağlantı olup olmadığını test etmemize gerek yoktur. &Ccedil;ü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 &#8216;CIKIS&#8217; komutunda olduğu gibi ne harici bir bağlantı kesilmesidir, ne bir network hatasıdır, ne de client&#8217;ın bağlantıyı kesmesidir. Gerçekte, Indy&#8217;nin bağlantı kesme denetimi ile müdahale edilmesiyle hiçbir döngü yapılmaz hale gelecektir.</p><p><strong>Komut Denetleyicileri (Command Handlers) </strong></p><p
align="justify">Indy 9.0 ile birlikte gelen bu özellik, TCP server tanımlamalarını çok kolaylaştırmıştır. Komut denetleyicilerini, server&#8217;ın &quot;Action List&quot;&#8217;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.</p><p
align="justify">Ö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&#8242;i bu şekildedir. Komut denetleyicileri binary verilerle uğraşabilirken, sadece yazı tabanlı komutlarla uğraşabilir.</p><p
align="justify">Indy 9&#8242;da <font
face="Courier New">TIdTCPServer</font>&#8216;da mevcut olan CommandHandlers özelliği, Indy 10 ile birlikte artık <font
face="Courier New">TIdCmdTCPServer</font> sınıfına alınmıştır. Yani eğer komut denetleyicilerini kullanmak istiyorsanız, <font
face="Courier New">TIdTCPServer</font> yerine <font
face="Courier New">TIdCmdTCPServer</font> bileşenini kullanmalısınız. Yine Indy 9&#8242;da <font
face="Courier New">CommandHandlersEnabled</font> isminde bir özellik mevcuttur. Eğer bu özellik True ise, öncelik komut denetleyicilerinde olacaktır. Yani <font
face="Courier New">OnExecute</font> olayı olsa bile, bu özellik True olduğunda ilgili komut denetleyicisi çalıştırılacaktır, <font
face="Courier New">OnExecute</font> çalıştırılmayacaktır. Ama komut denetleyicisi ya aktif değil ya da bilinmeyen bir komut ise <font
face="Courier New">OnExecute</font> çalıştırılır. Indy 10&#8242;da komut denetleyicileri ayrı bir bileşene taşındığından bu özelliği gerek kalmamıştır.</p><p
align="justify">Å?imdi şöyle bir örnek yapalım. &Uuml;ç adet komutumuz olsun:</p><ul><li><strong>TarihGoster: </strong>Server&#8217;ın tarihini göstersin.</li><li><strong>Yardim:</strong> Mevcut komutları göstersin.</li><li><strong>Cikis:</strong> Oturumu kapatsın ve bağlantıyı kapatsın.</li></ul><p
align="justify">Bu basit tanımlama için yeni bir uygulama oluşturalım. Foruma bir adet <font
face="Courier New">TIdCmdTCPServer</font> yerleştirelim. <font
face="Courier New">DefaultPort</font> olarak herhangi bir şey girebilirsiniz. Örneğimizde 8500 gibi bir port kullanalım. <font
face="Courier New">Active</font> özelliğini True yapalım, böylece uygulama çalışır çalışmaz TCP server çalışmaya başlayacak.</p><p
align="justify">Bileşenin <font
face="Courier New">CommandHandlers</font> ö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&#8217;dan özelliklerini değiştirebilirsiniz. <font
face="Courier New">Command</font> özelliğini Cikis olarak değiştirelim. Eğer server&#8217;ı 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.</p><p
align="justify"><font
face="Courier New">Disconnect</font> özelliğini True yapalım. Böylece bu komut çalıştığında oturum kapatılacak ve client ile bağlantı kesilecektir. <font
face="Courier New">Name</font> özelliğini bu örneğimiz için <font
face="Courier New">cmdCikis</font> olarak değiştirelim. <font
face="Courier New">NormalReply</font> altından <font
face="Courier New">Code</font> kısmına 200, <font
face="Courier New">Text</font> kısmına &quot;Gule gule&quot; 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.</p><p
align="justify">Bu şekilde çalışabilecek bir komut denetleyicisi oluşturmuş olduk.</p><p
align="justify">Å?imdi bu komutumuzu test edelim. Derleyip programı çalıştırdıktan sonra, komut satırından &quot;<font
face="Courier New">telnet 127.0.0.1 8500</font>&quot; şeklinde girelim. Karşımıza &quot;<font
face="Courier New">200 Welcome</font>&quot; 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 <font
face="Courier New">NormalReply</font> kısmına yazdığımız text ve code&#8217;dur. Tekrar telnet ile bağlantı kurup Cikis dışında bir şey girdiğinizde &quot;<font
face="Courier New">400 Unknown Command</font>&quot; gibi bir cevap ile karşılaşırsınız.</p><p
align="justify">Tebrikler ilk komut denetleyici tabanlı tcp server&#8217;ınızı oluşturdunuz. Å?imdi dilerseniz Yardim ve TarihGoster komutlarına da el atalım.</p><p
align="justify">Cikis komutunu tanımladığımız gibi Yardim komutunu da tanımlayalım. Tek fark olarak, <font
face="Courier New">Disconnect</font> özelliği False olmalıki, bu komut çalıştıktan sonra bağlantımız kesilmesin. <font
face="Courier New">NormalReply.Text</font> özelliği StringList özellik editörünü kullandığı için bibr Memo&#8217;nun ya da ListBox&#8217;ın içeriğini doldurur gibi komut yardım bilgilerini yazabilirsiniz.</p><p
align="justify">TarihGoster&#8217;i de Yardim komutu gibi tanımlıyoruz. Tek fark, <font
face="Courier New">NormalReply.Text</font> özelliğini tanımlamıyoruz. Ama <font
face="Courier New">Code</font>&#8216;u yine 200 yapalım. TarihGoster komut denetleyicisi seçili iken Object Inspector&#8217;dan yeni bir event ekleyelim. <font
face="Courier New">OnCommand</font> event&#8217;ına gelip çift tıklayalım. Eğer <font
face="Courier New">TIdCommand</font> bulunamadı gibi bir hata alırsanız uses kısmına <font
face="Courier New">IdCommandHandlers</font>&#8216;ı ekleyebilirsiniz. Event Handler&#8217;ımızın içeriğini aşağıdaki gibi dolduralım</p><pre>
<pre class="brush: delphi">procedure TForm1.IdTCPServer1TIdCommandHandler2Command(ASender: TIdCommand);
var
  DateFormat: string;
begin
  if ASender.Params.Count = 0 then
  begin
    DateFormat:= &#039;yyyy-mm-dd hh:nn:ss&#039;;
  end
  else
  begin
    DateFormat:= ASender.Params[0];
  end;
  ASender.Reply.Text.Text := FormatDateTime(DateFormat, Now);
end;</pre></pre><p
align="justify">Eğer komutumuzda ekstradan bir parametre yoksa kendimiz bir tarih formatı belirliyoruz. Aksi halde client&#8217;ın gönderdiği ilk parametreyi tarih formatı olarak kullanıyoruz. Ardından cevabımızı &quot;Text&quot; StringList&#8217;inin içine yerleştiriyoruz.</p><div
align="justify"><p> Bu makalemizde de sona geldik. Umarım faydası olmuştur. Bu 3 makale ile Indy&#8217;ye güzel bir giriş yaptığınızı düşünüyorum. Eğer vakit olursa Indy&#8217;de daha ileri konulara da el atmaya çalışacağız. Fikir ve eleştirilerinizi bekliyorum.</p><p>vesselam.</p></div><p
align="justify">Fatih Tolga Ata &copy; 2007</p><p
align="justify"><strong>Kaynaklar:</strong></p><ul><li>Indy 10 Yardım Dosyaları</li><li><a
href="http://www.indyproject.org/Sockets/Docs/Articles.aspx">IndyProject Makaleleri</a></li><li><a
href="http://www.atozed.com/indy/">Atozed Indy Makaleleri</a></li><li>Indy In Depth</li></ul></p></p></p></p><p><a
href="http://www.diyezon.com/?p=35">Bölüm 1 &#8211; Giriş</a></p><p><a
href="http://www.diyezon.com/?p=36">Bölüm 2 &#8211; Okuma ve Yazma</a></p><p><a
href="http://www.diyezon.com/?p=37">Bölüm 3 &#8211; Server</a></p> ]]></content:encoded> <wfw:commentRss>http://www.diyezon.com/indy-ile-network-programlama-3-server/feed/</wfw:commentRss> <slash:comments>9</slash:comments> </item> </channel> </rss>
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using memcached
Page Caching using memcached
Database Caching 1/19 queries in 0.033 seconds using memcached
Object Caching 437/472 objects using memcached

Served from: www.diyezon.com @ 2012-02-08 11:54:48 -->
