- Soru 
        Şu başlık dosyalarını nereden edinebilirim?
       
- Yanıt 
        Eğer sisteminizde bunlar mevcut değilse, muhtemelen zaten
        ihtiyacınız olmadığı içindir. Üzerinde çalıştığınız platformun
        belgelerini inceleyin. Eğer MS Windows ortamında derlemeye
        çalışıyor iseniz tek ihtiyacınız olan
        #include <winsock.h>.
       
- Soru 
        bind() "Address already in use" veya
        "Adres zaten kullanımda" mesajı verdiğinde ne yapmalıyım?
       
- Yanıt 
- Soru 
        Sistemdeki açık soketlerin listesini nasıl alabilirim?
       
- Yanıt 
        netstat komutunu kullanın. Bu komutla ilgili
        ayrıntılar için ilgili man sayfasını okuyun. Basit bir örnek
        vermem gerekirse sadece:
       
        yazarak bile epey mantıklı bir çıktı alabilirsiniz. Buradaki asıl
        mesele hangi soketin hangi program ile ilişkili olduğunu
        bulmak [141].
       
- Soru 
        Yönlendirme tablosunu nasıl görüntüleyebilirim?
       
- Yanıt 
        Kısaca: route komutunu çalıştırın
        (Linux için bu komutun yeri genellikle /sbin
        dizinidir) veya netstat -r komutunu deneyin.
       
- Soru 
        Eğer tek bir bilgisayarım varsa istemci-sunucu türünde programları
        nasıl çalıştırabilirim? Bir ağ programı yazabilmek için bir ağa
        ihtiyacım yok mu?
       
- Yanıt 
        Şanslısınız çünkü hemen hemen tüm işletim sistemleri bir tür
        geridönüş (loopback) aygıtı denilen sanal bir ağ aygıtı kullanır.
        Bu aygıt işletim sistemi çekirdeğine gömülü bir mekanizmadır ve
        tıpkı fiziksel bir ağ kartı gibi davranır böylece aynı makinada
        hem sunucu hem de istemci yazılımlar çalışabilir.
        (Bu sanal aygıt yönlendirme tablosunda "lo"
        olarak listelenir.)
       
        Bir örnek vermek gerekirse "goat" isimli bir
        bilgisayara giriş yaptığınızı varsayalım. Bir pencerede istemciyi
        diğerinde de suncuyu çalıştırabilirsiniz. Ya da sunucuyu şuna
        benzer bir komut ile arka planda çalıştırın:
        "server &" ve sonra aynı yerde istemciyi
        çalıştırın. Geridönüş aygıtı dediğimiz sanal aygıt sisteminizde
        mevcut ise (%99.9 olasılıkla mevcuttur) client goat
        veya client goat gibi bir komutla
        ("localhost"un sizdeki /etc/hosts
        dosyasında tanımlı olduğunu varsayıyorum) istemci yazılımın sunucu
        yazılım ile aynı makina üzerinde konuştuğunu gözlemleyebilirsiniz.
        Bir ağa ihtiyaç duymadan!
       
        Kısaca söylemek gerekirse ağa bağlı olmayan bir bilgisayarda
        yukarıda verilmiş olan örnek kodları çalıştırabilmek için
        herhangi bir değişiklik yapmanıza gerek yoktur. Yaşasınnn!
       
- Soru 
        Diğer tarafın bağlantıyı kestiğini nasıl tespit edebilirim?
       
- Yanıt 
        recv() işlevi öyle bir durumda 0
        değerini döndürür.
       
- Soru 
        Kendi "ping" programımı nasıl yazabilirim? ICMP nedir?
        Raw soketler ve SOCK_RAW ile daha ayrıntılı
        bilgiyi nerede bulabilirim?
       
- Yanıt 
        Raw soketler ile ilgili tüm sorularınızın cevabını W. Richard Stevens'ın
        "UNIX Network Programming" kitaplarında bulabilirsiniz. Lütfen
        kılavuzun  kitaplar bölümüne bakın.
       
- Soru 
    Bu programları MS Windows üzerinde nasıl derlerim?
   
- Yanıt 
- Soru 
     Bu programları Solaris/SunOS üzerinde nasıl derlerim?
     Derlemeye çalıştığımda linker sürekli hata veriyor!
    
- Yanıt 
- Soru 
      select() neden sinyal alır almaz düşüyor?
     
- Yanıt 
      Sinyaller genellikle bloklu sistem çağrılarının -1
      değerini döndürmesine ve errno değişkeninin de
      EINTR değerini almasına yol açar. Sinyal
      işlevi sigaction() ile ayarladığınızda,
      SA_RESTART kullanabilirsiniz. Böylece sistem
      çağrısı kesildiğinde onu yeniden başlatabilme ihtimali olur.
     
      Doğal olarak bu her zaman işe yaramaz.
     
      Bunun için önereceğim en iyi çözüm bir goto
      deyimine dayanır. Bildiğiniz gibi bu size yapısal programlama
      dersi veren profesörün tüylerinin diken diken olmasına yeter de
      artar bile, o halde neden kullanmayasınız!
     
select_restart:
    if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) {
        if (errno == EINTR) {
            // bir sinyal kesildi o halde yeniden baslat
            goto select_restart;
        }
        // gerçek hata ile burada ugras:
        perror("select");
    } 
      Tabii ki burada goto kullanmanız şart
      değil; kontrol için başka yapıları da kullanabilirsiniz,
      ancak ben özel olarak bu iş için goto deyiminin
      daha temiz olduğunu düşünüyorum.
     
- Soru 
      recv() işlevi için bir zamanaşımı mekanizmasını
      nasıl kurabilirim?
     
- Yanıt 
      select() işlevini
      kullanın! Bu işlev okuyacağınız soket tanımlayıcıları için bir
      zamanaşımı parametresi belirlemenize izin verir. Ya da bu işlevselliğin
      tamamını tek bir işlev içine şu şekilde gömmelisiniz:
     
 
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
int recvtimeout(int s, char *buf, int len, int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;
    // dosya tanımlayıcı listesini ayarla
    FD_ZERO(&fds);
    FD_SET(s, &fds);
    // zamanaşımı için struct timeval türündeki değişkeni ayarla
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    // veri gelene veya zamanaşımı dolana dek bekle
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // zamanaşımı!
    if (n == -1) return -1; // hata
    // veri burada olmalı, o halde normal şekilde recv() çağır
    return recv(s, buf, len, 0);
}
// recvtimeout() örneği:
    .
    .
    n = recvtimeout(s, buf, sizeof(buf), 10); // zamanaşımı 10 saniye
    if (n == -1) {
        // hata olustu
        perror("recvtimeout");
    }
    else if (n == -2) {
        // zamanaşımı!
    } else {
        // buf içine veri geldi
    }
    .
    . 
|  | Dikkat edin | 
|---|
 | 
          recvtimeout() işlevi zamanaşımı durumunda
          -2 değerini döndürür. Neden 0
          değil? Hatırlayacak olursanız 0 değeri
          recv() işlevi söz konusu olduğunda karşı
          tarafın bağlantıyı kestiği anlamına geliyordu. Bu değer
          kullanılmış olduğu için ve -1 de hata
          anlamına geldiğinden zamanaşımı durumunu göstermesi için
          -2 değerini seçtim.
         |  
 
- Soru 
        Veriyi soket üzerinden göndermeden önce nasıl şifreleyip sıkıştırabilirim?
       
- Yanıt 
        Şifrelemeyi gerçekleştirmenin kolay yollarından biri SSL
        (secure sockets layer - güvenlik soketleri katmanı) kullanmaktır
        ancak bu konu bu kılavuzun kapsamı dışındadır.
       
        Ancak eğer sıkıştırma ve şifreleme algoritmalarınızı kullanmak
        istiyorsanız verinin her iki uçta da adım adım işlendiğini
        düşünmek işinizi kolaylaştıracaktır. Her adım veriyi belli bir
        şekilde dönüştürür.
       - sunucu veriyi bir dosyadan (veya bir yerlerden) okur, 
- sunucu veriyi şifreler (bu kısmı siz ekleyeceksiniz), 
- sunucu şifrelenmiş veriyi send() ile yollar. 
 Şimdi de öteki tarafa bakalım: - 
            istemci recv() ile kendisine yollanan
            şifreli veriyi alır,
           
- 
            istemci şifreli veriyi çözer yani deşifre eder (bu kısmı siz
            ekleyeceksiniz).
           
 
        Yukarıda şifreleme/deşifreleme yaptığınız aşamada sıkıştırma/açma
        işlemleri de yapabilirsiniz. Ya da hem şifreleme hem de sıkıştırma
        yapabilirsiniz! Yalnız aklınızda bulunsun, bir veriyi eğer
        şifreleyecekseniz önce sıkıştırın sonra şifreleyin [142]  :)
        Yani istemci, sunucunun uyguladığı dönüşümlerin tersini
        uygulayabildiği sürece araya istediğiniz kadar dönüşüm, işlem,
        işlev, vs. sokabilirsiniz.
       
        Tek yapmanız gereken verdiğim örnek kodda verinin gönderildiği
        ve alındığı kısımları tespit bunların öncesine şifreleme
        işlemini yerleştirmek.
       
- Soru 
        Bir sürü yerde gördüğüm şu "PF_INET" de nedir?
        AF_INET ile bir bağlantısı var mıdır?
       
- Yanıt 
- Soru 
        İstemciden kabuk komutlarını kabul edip onları çalıştıran bir
        sunucu yazılımını nasıl geliştirebilirim?
       
- Yanıt 
        Basit olsun diye varsayalım ki istemci connect()
        ile bağlanıyor, send() ile veriyi gönderiyor ve
        close() ile bağlantıyı kesiyor (yani istemci
        tekrar bağlanana dek bir sistem çağrısı söz konusu olmuyor).
       İstemcinin izlediği süreç şöyledir: - connect() ile sunucuya bağlan, 
- send("/sbin/ls > /tmp/client.out"), 
- close() ile bağlantıyı kes. 
 
        Bu arada sunucu gelen veri ile muhatap olur ve onu çalıştırır:
       - accept() ile bağlantıyı kabul et, 
- recv(str)  ile komut dizisini al, 
- close() ile bağlantıyı kes, 
- system(komut) ile komutu çalıştır. 
 |  | Dikkat | 
|---|
 | 
          İstemcinin sunucuya ne yapacağını söylemesi uzaktan kabuk erişimi vermek
          demektir. Böyle bir yetkiye sahip olan bir insan kötü şeyler yapabilir.
          Mesela yukarıdaki gibi bir programda istemci
          "rm -rf ~" gibi bir komut gönderirse ne olur?
          Sizinle ilişkili alandakı tüm dosyalar silinir, işte budur
          olacağı!
         |  
 
        Bu yüzden akıllı olun ve kesinlikle güvenli olduğunuz programlar
        haricinde uzaktaki istemcinin sizin sunucunuzda hiçbir şey
        çalıştırmasına izin vermeyin, örneğin foobar
        komutu gibi:
       
if (!strcmp(str, "foobar")) {
    sprintf(sysstr, "%s > /tmp/server.out", str);
    system(sysstr);
} 
         foobar güvenilir ve sorunsuz bir komut
         olabilir ama gene de şüpheci olmalısınız ya istemci
         "foobar; rm -rf ~" gibi bir komut dizisi
         yollarsa? En güvenli yöntemlerden biri komuta verilen
         parametrelerdeki alfanümerik olmayan her karakterin (boşluk
         dahil) başına önceleme karakteri ("\")
         koyan bir işlev yazmaktır.
        
         Gördüğünüz gibi sunucu tarafında istemcinin gönderdiği komutları
         çalıştırma gibi bir durum söz konusu olunca güvenlik çok önemli
         bir mesele haline gelmektedir.
        
- Soru 
         Bir yığın veriyi bir seferde yollamaya çalışıyorum ama diğer
         taraftan recv() ile okumaya kalktığımda
         sadece 536 veya 1460 byte geldiğini görüyorum. Ancak aynı
         denemeyi kendi makinamın üzerinde iki farklı pencere açıp
         yaptığımda sorunsuz olarak veri yollayıp alabiliyorum.
         Bunun sebebi nedir?
        
- Yanıt 
         MTU sınırını -- fiziksel ortamın bir seferde kaldırabileceği
         azami yük miktarını aşıyorsunuz. Makinanızdaki sanal geridönüş
         aygıtı sürücüsü 8K ya da daha fazlasını bir seferde sorunsuz
         olarak iletebilir. Ancak Ethernet bir seferde başlık bilgisi ile
         birlikte en fazla 1500 byte taşıyabilir ve siz de bu limiti
         aşmış durumdasınız. Modem üzerinden veri yollamaya kalktığınızda
         576 byte sınırı vardır ve yine bunu tek seferde geçerseniz
         sorun yaşarsınız.
        
         Öncelikli olarak tüm veriyi yolladığınızdan emin olmalısınız.
         (Bunun için lütfen
          sendall()
         ile ilgili ayrıntılı açıklamalara bakın.) Bundan eminseniz buna
         ek olarak verinin tamamını okuyana dek bir döngü içinde
          recv() işlevini çağırmanız gerekir.
        
         recv() işlevini defalarca çağırarak verinin
         tamamını alma ile ilgili ayrıntılı açıklamalar için lütfen
          Veri Paketlemesi Hazretleri bölümünü okuyun.
        
 
- Soru 
         Bilgisayarımda MS Windows çalışıyor ve elimde fork()
         gibi bir sistem çağrısı olmadığı gibi struct sigaction
         şeklinde bir yapı da yok. Ne yapabilirim?
        
- Yanıt 
         Eğer varsalar POSIX kitaplıkları içindedirler ve bunlar da
         derleyiciniz ile gelmiş olabilir. Ben Windows kullanmıyorum bu
         yüzden size kesin cevap veremeyeceğim. Fakat hatırladığım kadarı
         ile Microsoft'un da kullandığı bir POSIX uyumluluk katmanı vardı
         ve işte aradığınız fork() olsa olsa oradadır.
         (Hatta belki sigaction bile.)
        
         VC++ ile gelen belgeleri "fork" veya "POSIX" için bir tarayın ve
         size hangi ipuçlarını verdiğine bir bakın.
        
         Eğer mantıklı bir şey çıkamazsa fork()/sigaction
         ikilisini bırakın ve şunu deneyin: CreateProcess().
         Açıkçası ben CreateProcess() işlevi tam olarak
         nasıl kullanılır bilmiyorum -- milyarlarca argüman alıyor olmalı ama
         VC++ belgelerinde hepsi açıklanıyor olmalı.
        
- Soru 
         TCP/IP üzerinde veriyi güvenli ve şifreli bir şekilde nasıl
         iletebilirim?
        
- Yanıt 
- Soru 
         Bir güvenlik duvarının arkasındayım -- diğer taraftaki insanların
         benim IP adresimi öğrenip benim sistemime bağlanmalarını nasıl
         sağlarım?
        
- Yanıt 
         Maalesef bir güvenlik duvarının amacı tam da dışarıdaki
         insanların doğrudan sizim makinanıza bağlanmasını engellemektir.
         Bu şekilde içeri bağlanmaları çoğu durumda bir güvenlik açığı
         olarak kabul edilir.
        
         Bu tabii ki talebinizin imkânsız olduğu anlamına gelmez yani
         gene de güvenlik duvarı üzerinde connect()
         yapabilirsiniz tabii bir "maskeleme" ya da "NAT" veya benzer
         bir şey söz konusu ise. Programlarınızı daima bağlantıyı sizin
         başlatacağınız şekilde tasarlayın, böylece işiniz kolay olur.
        
         Eğer bu sizi tatmin etmiyorsa sistem yöneticilerinize size özel
         bir delik açmalarını söyleyebilirsiniz. Güvenlik duvarı,
         üzerindeki NAT yazılımı ile ya da vekil tarzı bir şey ile bunu
         gerçekleştirebilir.
        
         Ancak lütfen unutmayın ki güvenlik duvarı üzerindeki bir delik
         hafife alınabilecek bir durum değildir. Kötü niyetli kişilere
         erişim hakkı vermediğinizden emin olmalısınız. Eğer bu konularda
         henüz acemiyseniz bana inanın ki güvenli programlar yazmak hayal
         edebileceğinizden çok daha zordur.
        
         Sistem yöneticilerinizin benden nefret etmesine yol açmayın.
         ;-)