Hele şükür bu aşamaya gelebildik. Artık biraz programlamadan bahsedebiliriz.
Bu bölümde soket arayüzleri tarafından kullanılan pek çok veri türünü ele
alacağım çünkü bunlar gerçekten önemli.
Kolay olanlarla başlayalım: bir soket tanımlayıcı. Bir soket tanımlayıcı
aşağıdaki türdendir:
Evet, gayet klasik, alışık olduğumuz basit bir int.
İşte bu aşamadan itibaren işler biraz garipleşmeye başlıyor bu yüzden de
dikkatlice okuyun ve bana güvenin. İlk bilmeniz gereken: iki tür byte
sıralaması vardır: en önemli baytın (ki buna bazen öktet de denir)
önce geldiği sıralama veya en önemli baytın sonra geldiği sıralama.
Bu sıralamalardan birincisine "Ağ Bayt Sıralaması" (Network Byte Order)
[136]
denir. Bazı makinalar içsel olarak veriyi kendi belleklerinde bu şekilde
depolar, bazıları ise bu sırayı dikkate almaz. Bir şeyin Ağ Bayt Sıralaması'na
göre sıralanması gerektiğini söylediğimde
htons() gibi
bir işlev çağırmanız gerekecek ("Konak Bayt Sıralaması"ndan [Host Byte Order]
"Ağ Bayt Sıralaması"na dönüştürebilmek için). Eğer "Ağ Bayt Sıralaması"ndan
bahsetmiyorsam o zaman ilgili veriyi olduğu gibi yani
"Konak Bayt Sıralaması" düzeninde bırakmanız gerekir.
My First Struct™ -- struct sockaddr.
Bu veri yapısı pek çok türde soket için soket adres bilgisini barındırır:
struct sockaddr {
unsigned short sa_family; // adres ailesi, AF_xxx
char sa_data[14]; // protokol adresinin 14 byte'ı
};
sa_family pek çok değerden birini alabilir ama
bizim örneğimizde AF_INET olacak.
sa_data ise soketle ilgili hedef adres ve port
numarası bilgilerini barındırır. Bunun garip olduğunu kabul ediyorum, yani
herhalde bu bilgiyi kendi ellerinizle paketleyerek sa_data
değişkenine yerleştirmek istemezsiniz değil mi?
struct sockaddr ile başa çıkabilmek için programcılar buna
paralel bir yapı tasarlamışlar: struct sockaddr_in ("in"
"Internet" anlamına geliyor.)
struct sockaddr_in {
short int sin_family; // Adres ailesi
unsigned short int sin_port; // Port numarası
struct in_addr sin_addr; // Internet adresi
unsigned char sin_zero[8]; // struct sockaddr ile aynı boyda
};
Bu yapı soket adresi elemanlarına erişmeyi kolaylaştırır. Dikkat etmeniz
gereken bir nokta:
sin_zero,
[137]
memset() işlevi kullanılarak tamamen sıfır ile
doldurulmalıdır. Buna ek ve daha da önemli olarak bir
struct
sockaddr_in göstergesi
struct sockaddr göstergesine
dönüştürülebilir ve tersi de doğrudur. Yani her ne kadar
socket() işlevi
struct sockaddr* şeklinde
bir veri beklese de siz gene de
struct sockaddr_in kullanıp
son anda gerekli dönüştürmeyi yapabilirsiniz! Ayrıca
sin_family
değişkeninin de
struct sockaddr yapısındaki
sa_family değişkenine karşılık geldiğini ve
"
AF_INET" olarak ayarlanması gerektiğini unutmayın.
Son olarak
sin_port ve
sin_addr
değikenlerinin de Ağ Byte Sırasında bulunmaları gerektiğini unutmayın!
"Fakat," diye itiraz edebilirsiniz, "nasıl olur da tüm yapı yani
struct in_addr sin_addr Ağ Bayt Sıralamasına göre
dizilebilir ki?" Bu soru tüm zamanların en kötü
union'larından biri olan struct in_addr
yapısının dikkatli olarak incelenmesini gerektirir:
// Internet adresi (tarihi sebeplerden ötürü korunmakta)
struct in_addr {
unsigned long s_addr; // 32-bit yani 4 bytes
};
Evet, bir zamanlar kötü bir union idi. Neyse ki
geçmişte kaldı. Eğer ina değişkenini
struct sockaddr_in türünden tanımladı iseniz
ina.sin_addr.s_addr 4 byte'lık Internet
adresini gösterir (Ağ Bayt Sıralamasında). Aklınızda bulunsun eğer
sizin sisteminiz kahrolası struct in_addr
union'ını kullanıyor olsa bile yine de 4 byte'lık
Internet adresine tamamen benim yaptığım gibi ulaşabilirsiniz
(bunu #define'lara borçluyuz).