C++ Üzerinde Soket Programlama

Bu arada unutmadan, eğer ben udp kullanarak tek bir soketle bağlantır kuracağım derseniz connect fonksiyonunu kullanabilirsiniz. Bu durumda udp bağlantı olduğu halde send ve recv fonksiyonlarını da kullanabilirsiniz.  Ben bu kullanımı tercih ediyorum ama ihtiyaca göre tabi…

                                                           *    *     *

İsterseniz biraz istemci sunucu mimarisinden bahsedelim. Daha önce de belirttiğim gibi sunucu belli bir portu dinler ve bağlanan istemcilere hizmet sunar. Ancak  ortada küçük bir problem var, sunucu accept fonksiyonu ile bağlantı beklerken ( accept fonksiyonu blokeli çalışır) başka bir iş yapamaz.

Yani 10 tane istemci bağlanmaya çalışıyor diyelim. İlk istemci bağlantı isteği gönderdiğinde accept ile kabul edilir, ve ardından tekrar accept fonksiyonu çalışır.

Peki accept fonksiyonu blokeli çalışıyorken aynı zamanda bağlantı kurduğumuz istemcilere nasıl hizmet vereceğiz?

Bunun bir yöntemi soketi blokeli moddan çıkarıp (fcntl ile) sürekli genel bir döngü içinde bağlantı isteği olup olmadığını kontrol etmektir.

Ancak bu yol işlemcinin daima %100 kullanılması demektir ve kullanışsız olmasının yanında verimsiz bir yoldur. Ayrıca recv fonksiyonu da ağ tamponuna bir veri gelene kadar bloklanır ve bekler, yani biz istemcilerden birinden bir veri alacaksak diğerlerine ondan veri gelene kadar hizmet edemeyiz.

Bu sorun elbette çözümsüz değildir, Windows ortamında gelen her bağlantı için CreateThread fonksiyonu ile yeni bir thread yaratılarak tüm istemcilere hizmet verilebilir.

Bu yöntem 10 istemciye kadar normal görülebilir ancak istemci sayısı arttıkça sunucu yerlerde sürünmeye başlar. İşte bu durum düşünülerek işletim sistemleri tarafından sağlanan bir select fonksiyonu tasarlanmıştır. Select fonksiyonu aslında bir dosya fonksiyonudur, linux da her türlü dosyayla birlikte kullanılabilir ancak windows da sadece soketler için kullanılabilir.

Select fonksiyonu birçok soketi aynı anda gözlemleyebilir ve herhangi bir sokette hareketlilik olduğunda bunu bize bildirir. Diğer fonksiyonlardan biraz daha karmaşık olduğu halde çok yararlı bir fonksiyondur..

Select fonksiyonu 3 tane dosya tanımlayıcı kümesi ni parametre olarak alır, bunu biraz açıklayalım… Dosya tanımlayıcılar int türden nesnelerdir. Her dosya tanımlayıcı bir dosya ile ilişkilidir. Örneğin SOCKET dosya tanımlayıcısı socket() fonksiyonu ile oluşturulur. Yukarıdaki örneklerde kullandığımız skt nesnesi aslında soketin kendisi değil onun dosya tanımlayıcısıdır. Yani aslında skt, int türden bir tamsayıdır. Bu tam sayı tek bir dosyaya ilişkin olduğu için istediğimiz fonksiyonda bu sayı ile o dosyaya yani sokete erişebiliriz.

Fopen ile açılan bir dosya FILE * türünde bir dosya handlesi döndürüyordu, bu handle dosya tanımlayıcısı değildir ancak dosya tanımlayıcılar üzerine kurulmuş daha üst seviye bir arayüzdür. Aslında sistemde açılan her dosyanın bir dosya tanımlayıcısı vardır. Giriş çıkış işlemleri en temel seviyede bu dosya tanımlayıcıları kullanarak dosyaya yazma ve okuma işlemleridir.

Dosya tanımlayıcılardan bahsettikten sonra dosya tanımlayıcı kümesinden kastımızın ne olduğunu söyleyeyim…Aslında dosya tanımlayıcı kümesi int türden bir dizi olarak düşünülebilir. Biz bir kümeye dosya tanımlayıcı ekleyebiliriz ya da silebiliriz. Yani içnide birden fazla dosya tanımlayıcı barındıran bir kümedir bu. Bu dosya tanımlayıcılar pekala soketler olabilir.

İşte select fonksiyonu bu şekilde 3  dosya tanımlayıcı küme alır. Bu kümeleri sürekli gözlemler, bir dosya tanımlayıcı uyarı gönderirse bunu hemen bize bildirir. Yani biz select fonksiyonuna 1000 elemanlı bir soket kümesini verdiğimizde bu fonksiyon hangisinin o anda işlem yapmak için hazır olduğunu bize bildirebilir.  Ne güzel değilmi J

Peki neden üç tane küme alır, tek küme alması yeterli değil mi?  İşte aldığı ilk dosya tanımlatıcı kümesi readfds (sanırım “read file descriptor set”) dir, ikincisi writefds ( “write file descriptor set”) , sonuncusu ise exceptfds (“execpt file descriptor set”) dir.

Bunları kısaca açıklayayım, select fonksiyonunun kendisine verilen dosya tanımlayıcı kümelerini kontrol ettiğini biliyoruz, işte readfds kümesindeki tanımlayıcılardan  birisi okunabilir (readability) durumda ise yani bir veri gelmiş ise , select fonksiyonu uyarı verir..

Aynı şekilde writefds kümesindeki tanımlayıcılardan birisi yazılabilir (writability) durumda ise select fonksiyonu yine uyarı verir.

Yani biz herhanngi bir dosyaya okuma ya da yazma yapıp yapamayacağımnızı merak ediyorsak bu dosyayı bu iki tanımlayıcı kümesinden birisine eklememiz yeterli, select fonksiyonu bizi hemen uyarır ve biz de hangi dosyanın ne uyarısı verdiğine bakarak okuma ya da yazma işlemimizi yapabiliriz.

Eğer listen fonksiyonu ile bir soketi dinliyorsak ve yeni bir bağlantı gelmişse bu durumda yine readfds uyarı verir. Yani readfds ye eklediğimiz soket uyarı veriyorsa bu salt yeni bir veri geldi demek değildir. Tabi bunun kontrolünü de programımızda yapacağız…

Eğer kafanız karıştıysa önemsemeyin, örneklerde daha iyi anlayacaksınız… Son olarak exceptfds ise hata kontrolü için kullanılır, dosya tanımlayıcılardan birinde harici (except) bir durum olursa bu dosya tanımlayıcı kümesi sayesinde  bunu öğreniriz.

Select fonksiyonunun dosya tanımlayıcı kümeleri dışında iki parametresi daha vardır.Bunlardan biri yine gereksiz bir parametredir, windows programlarında 0 olarak geçilir ve bsd yani unix soketlerine uyumluluk için eklenmiştir (ilk ağ sistemi ve programlaması bsd işletim sisteminde olduğu için diğer sistemlere bundan geçmiştir).

Linux da bu parametreye en büyük soket tanımlayıcısının değeri geçilir.

Select fonksiyonunun son parametresi ise zaman aşımı süresidir. struct timeval türünden bir yapıdır, bu yapının iki elemanı vardır, birisi tv_sec diğeri tc_usec. Tv_sec saniyeyi, tv_usec ise mikro saniyeyi tutar.

Biz sadece tv_sec e saniyeyi yazsak yeter, zaten bir mikro saniye 10 üzeri -6 saniye gibi çok küçük bir değerdir.

Bu parametreyi neden kullanacağımızı anlatayım, mesela bir chat sunucususunuz, gelen tüm bağlantıları okuma  soket tanımlayıcı kümesine koydunuz  ve okumak için gelecek bir veri bekliyorsunuz. Ancak ne bir şey yazan var ne de yeni gelen birileri, eğer böyle bir durumda sunucuyu kapatır çeker giderim diyorsanız bu parametreye 0 dışında bir bekleme süresi koyarsınız J

Eğer tv_sec elemanına 100 değerini verirseniz soketlerden hiç birinde 100 saniye içinde bir etkinlik olmazsa select fonksiyonu sona erer.

Eğer bu parametreyi 0 olarak geçerseniz asla zaman aşımı olmaz, dikkat edelim, yapı elemanlarının içine 0 değerini koymayacağız direkt bu parametreye 0 değerini vereceğiz…

Evet sanırım artık şu select fonksiyonunu yazabiliriz…

Select(0,*readfds,*writefds,*exceptfds,zamanasimi);

 Tabi fonksiyon burda dosya tanımlayıcı kümesinin adresini alır kendisini değil, standart değişkenler dışında hiçbir değişken doğrudan parametre olmaz zaten ;)

Fonksiyonumuzun tam olarak ne işe yaradığını anladığımıza göre artık dosya tanımlayıcı kümelerini nasıl tanımlayacağımızı öğrenebiliriz

Dosya tanımlayıcı kümeleri  fd_set türündedir. Fd_set türü winsock.h içinde şu şekilde typedef edilmiştir.
                
              
            
Tarih:
Hit: 10336
Yazar: Tugberk



Yorumlar


Siftahı yapan siz olun
Yorum yapabilmek için üye girişi yapmalısınız.