Sıkça Sorulan Sorular
Önceki Beej'in Ağ Programlama Kılavuzu Sonraki
Sıkça Sorulan Sorular
Soru Şu başlık dosyalarını nereden edinebilirim?
Soru bind() "Address already in use" veya "Adres zaten kullanımda" mesajı verdiğinde ne yapmalıyım?
Soru Sistemdeki açık soketlerin listesini nasıl alabilirim?
Soru Yönlendirme tablosunu nasıl görüntüleyebilirim?
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?
Soru Diğer tarafın bağlantıyı kestiğini nasıl tespit edebilirim?
Soru Kendi "ping" programımı nasıl yazabilirim? ICMP nedir? Raw soketler ve SOCK_RAW ile daha ayrıntılı bilgiyi nerede bulabilirim?
Soru Bu programları MS Windows üzerinde nasıl derlerim?
Soru Bu programları Solaris/SunOS üzerinde nasıl derlerim? Derlemeye çalıştığımda linker sürekli hata veriyor!
Soru select() neden sinyal alır almaz düşüyor?
Soru recv() işlevi için bir zamanaşımı mekanizmasını nasıl kurabilirim?
Soru Veriyi soket üzerinden göndermeden önce nasıl şifreleyip sıkıştırabilirim?
Soru Bir sürü yerde gördüğüm şu "PF_INET" de nedir? AF_INET ile bir bağlantısı var mıdır?
Soru İstemciden kabuk komutlarını kabul edip onları çalıştıran bir sunucu yazılımını nasıl geliştirebilirim?
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?
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?
Soru TCP/IP üzerinde veriyi güvenli ve şifreli bir şekilde nasıl iletebilirim?
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?

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
Dinleyici soketler üzerinde setsockopt() ile birlikte SO_REUSEADDR seçeneğini kullanmalısınız. bind() ile ilgili bölümü ve select() ile ilgili bölümü okuyup örnekleri inceleyin.
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:
$ netstat
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
En basit yöntem şu: MS Windows'u silin ve Linux ya da BSD işletim sistemini kurun. };-) Peki peki tamam, o kadar ısrarcı iseniz Windows Programcılarının Dikkat Etmesi Gerekenler bölümüne bakın.
Soru
Bu programları Solaris/SunOS üzerinde nasıl derlerim? Derlemeye çalıştığımda linker sürekli hata veriyor!
Yanıt
Linker hatası alabilirsiniz, çünkü genellikle Sun Microsystems ortamında soket kitaplıkları otomatik olarak bağlanmaz. Ayrıntılı bilgi için Solaris/SunOS Programcılarının Dikkat Etmesi Gerekenler bölümüne bakın.
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.
  1. sunucu veriyi bir dosyadan (veya bir yerlerden) okur,
  2. sunucu veriyi şifreler (bu kısmı siz ekleyeceksiniz),
  3. sunucu şifrelenmiş veriyi send() ile yollar.
Şimdi de öteki tarafa bakalım:
  1. istemci recv() ile kendisine yollanan şifreli veriyi alır,
  2. 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
Evet, elbette. Bunun için lütfen socket() -- Al Şu Dosya Tanımlayıcıyı! bölümüne bakın.
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:
  1. connect() ile sunucuya bağlan,
  2. send("/sbin/ls > /tmp/client.out"),
  3. close() ile bağlantıyı kes.
Bu arada sunucu gelen veri ile muhatap olur ve onu çalıştırır:
  1. accept() ile bağlantıyı kabul et,
  2. recv(str) ile komut dizisini al,
  3. close() ile bağlantıyı kes,
  4. 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
OpenSSL projesine bir göz atın.
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. ;-)


[141] Ç.N.: lsof komutu bu konuda size yardımcı olabilir, ayrıntılar için man sayfasına bakın. :-)
[142] Ç.N.: Yazarın burada tek bir cümle olarak söyleyip geçtiği şey güvenlik açısından göründüğünden çok daha fazla önemlidir, kullanacağınız şifreleme algoritmasına çok dikkat edin ve mutlaka şifrelemeden önce sıkıştırma işlemini uygulayın.
Önceki Üst Ana Başlık Sonraki
RFC'ler Başlangıç Son söz ve Yardım Çağrısı
Bir Linux Kitaplığı Sayfası