C++ Üzerinde Soket Programlama

#include

#include

int main(){

Windows da soket programlama WSAStartup fonksiyonu ile başlar ve WSACleanup fonksiyonu ile biter. Linux da böyle bir olay yok tabi herşey doğal. Bill amcanın işleri işte..

WSAStartup iki parametre alır, birisi winsock bilgilerini tutacak olan bir yapının adresi WSADATA türünde, diğeri ise winsock un versiyon numarası. Bu fonksiyonla hiçbir işimiz yok, her programda standart olarak yazacaz o yüzden fazla uzatmıyorum ve yazıyorum..

WSADATA wsdata;                       //bilgiler buraya yazılacak, biz ilgilenmiyoruz.

WSAStartup(MAKEWORD(2,0),&wsdata);  // makeword verilen versiyon numarasını word e çevirmek için kullandığımız bir makro.

Gelelim soket yaratma olayına, öncelikle soket değişkenini tanımlamamız gerekiyor. Türü ne mi dersiniz, SOCKET J

SOCKET skt;

Soketimizi tanımladık, SOCKET tipi aslında basit bir tamsayıdır, int ten farkı yoktur ancak winsock.h da SOCKET olarak typedef edilmiştir. Soketin bir dosya olduğunu söylemiştik,  soketi yaratan fonksiyon da dosya numarası ile geri döner, işte bu numara skt değişkenimize atanacak.

İsterseniz soketi oluşturan fonksiyonuda yazalım, adı ne dersiniz?, socket J

Skt=socket(AF_INET, SOCK_STREAM,0);  

AF_INET standarttır adres family demek , SOCK_STREAM soketin türü mesela tcp ise SOCK_STREAM, udp ise SOCK_DGRAM olacak. Son parametre de her zaman 0 olacak, soket tipi parametresinden  uygun tipi seçmesi için varsayılan 0 dır.

Fonksiyon başarılı olursa soketin numarasını, başarısız olursa -1 değerini döndürür. Bence bir programın kalitesi o programda hata denetiminin kullanılıp kullanılmadığı ile anlaşılır. Bu yüzden muhakkak hata kontrolü uygulayın. Yazacağınız çok bir şey değil..

if((Skt=socket(AF_INET, SOCK_STREAM,0))==-1) printf(“soket açılamadı dostum üzgünüm\n”);

Evet artık bir tcp soketimiz var, ancak bu sokete hangi sunucuya bağlanacağını, hangi porttan bağlanacağını ya da hangi portu dinleyeceğini nasıl anlatacağız?

İşte bunun için bir veri yapısı kullanılır, bu soket ile veri yapısı bind edilir ve artık o soketin herşeyi belli olmuştur, veri alışverişine hazırdır.

Bahsettiğim veri yapısı sockaddr_in , bu yapının tanımlamasını yapalım ve elemanlarını dolduralım..

struct sockaddr_in bilgiler;  // bilgiler in elemanlarını soket bilgileri ile dolduralım ki soketimiz nereye bağlanacağını vs bilsin..

bilgiler.sin_family = AF_INET;

bilgiler.sin_port = htons(PORT);

iki bilgisini doldurduk bile, sin_family her zaman af_inet kalacak,  Port yazan yere portumuzu yazacaz mesela 5000, htons, “ host to network short” demektir. Yani ağda kullanılan standart yazım sistemi ile intel işlemcileri terstir. İntel işlemcileri little endian, kullanır yani mesela intel işlemcisi ile belleğe AC F5 baytlarını yolla dersneiz belleğe F5 AC diye yazar. Ancak ağda kullanılan standart böyle olmadığı için 2 baytlık verileri htons 4 baytlıkları htonl ile ağ standartına çevirmemiz lazım..

bilgiler.sin_addr = INADDR_ANY;

sin_addr elemanı sunucu adını yani ip veya alan adını alır, tabi aslında aldığı değer long türden bir tamsayıdır ancak biz alan adını uygun şekilde düzenleyip içine atacaz.  Burada önemli bir ayrıntı var, eğer programımız sunucuysa soket bilgi yapısının  sunucu adresi bölümüne bir şey yazmanın bir anlamı var mı? Zaten kendisi sunucu ve belli bir portu dinleyecek ve istemciler ona bağlanacak.

İşte eğer sunucu iseniz sin_addr elemanına INADDR_ANY değerini geçmelisiniz. Bu herhangi bir adres çağrışımı yapıyor ama kendi adresimizi yazar bu elemana..

Evet sunucuysak bu elemanı doldurmak kolay, peki ya istemciysek, mesela adam www.tahribat.com diye adresi girdi, bunu long türden bir tamsayı olan adres bilgisine nasıl çeviririz, ya da 88.54.256.55 gibi bir ip adresi girildi bunu nasıl bu elemana aktaracağız.

İşte burada işler biraz karışıyor ama ilerde daha iyi anlayacaksınız, şimdilik bu vereceğim çevirme fonksiyonlarına fazla takılmayın , zaten bunlar rutin işlerdir, programı yazarken sadece bir kere doldurulur…

Gelelim olayın ayrıntılarına, kullanıcıya sunucu adını girmesini söyledik ve bekliyoruz, kullanıcı herhangi bir string girdi ve biz bu stringi char IP[30] gibi bir diziye aktardık. Bu sunucu adını çözmek için gethostbyname() fonksiyonunu kullanacağız..

Gethostbyname fonksiyonu sizden bir string alır ve bu adı çözerek bir ip bilgisi çıkarır, bu ip ile ilgili çeşitli bilgileri kendi içinde bir yapıya aktarır ve bu yapının adresini bize geri döndürür. Geri döndürdüğü yapının türü “struct hostent” dir. Bu adresi alıp yapıyı kullanabilmemiz için tahmin edeceğiniz gibi bu türden bir yapı tanımlamamız lazım tabi pointer olarak, yani yapının kendisini değil adresini gönderir fonksiyon …

struct hostent *sunucu_adi;

tanımlamamızı yaptıktan sonra ;

 sunucu_adi = gethostbyname(IP);

diyoruz ve *sunucu_adi yapımızda artık istediğimiz adresimiz var. Hostent yapısının içeriğini söylemeye gerek duymuyorum daha fazla kafalar karışmasın diye, biz sadece h_addr elemanıyla ilgilenecez çünkü, aslında bakarsanız  hostent yapısının h_addr diye bir elemanı yoktur J 

#define h_addr *h_addr_list   ya da

#define h_addr h_addr_list[0]

olarak tanımlanmıştır.. Dediğim gibi biz sunucu_adi->h_addr  ile ilgileneceğiz.. Şimdi aldığımız bu adresi soketimizin bilgiler yapısına aktaralım…

bilgiler.sin_addr = *((struct in_addr *)sunucu_adi->h_addr);

derleyicinin uyarısını kesmek için sunucu_adi yapı adresimizi in_addr yapi adresine çeviriyoruz. Basit bir pointer dönüşümü fazla takmayın. En başta neden yıldız var diyenler için açıklayayım merak etmeyenler alt paragraftan devam etsin J   #define ettiğimiz h_addr_list aslında bir gösterici göstericisidir, yani tipi char  ** dır. Dolayısıyla h_addr de aslında doğrudan değer değil bir bellek adresidir. Bu yüzden * içerik operatörüyle gerçek sunucu adresi bilgisini yapımıza aktarıyoruz..

Son derece gereksiz bir işlem kaldı geriye,

memset(&(bilgiler.sin_zero), 0,0) ;

ben yine de ilgilenenler için kısaca bahsedeyim, isteyen bu bölümü de okumayabilir . Aslında soketin bilgilerini struct sockaddr_in yapısı değil struct sockaddr yapısı tutar. Ancak bu yapının kullanımı daha zor olduğu için onun yerine ona eşdeğer olan ama kullanımı daha kolay olan struct sockaddr_in yapısı tasarlanmış.

 Ancak sockaddr yapısı 16 bayt iken sockaddr_in yapısı 8 bayt kalır mıJ düzen bozulmasın diye sockaddr_in yapısına bir eleman daha eklenmiş, o da : unsigned char sin_zero[8] elemanı, adı üstünde tamamen boşluk doldurmak için olan bu eleman sıfırla doldurulmalıdır, daha doğrusu doldurulsa iyi olur. Bu yüzden memset ile içini sıfırlıyoruz…

Sonunda sockaddr_in yapısı da bitti. Elimizde bir soketimiz bir de yapımız var ama bunlar henüz tanışmadı J tanıştırmak tabiki bize düşüyor…

Nasıl mı ? tabi ki bind fonksiyonuyla . Bind etmek bağlamak, birleştirmek gibi bir anlam taşır. Yani soketimiz ile soket bilgilerini taşıyan yapımızı bind ediyoruz…

Tarih:
Hit: 10591
Yazar: Tugberk



Yorumlar


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