bind() -- Hangi Port Üzerindeyim?
Önceki Sistem Çağrıları veya Felaketleri Sonraki
bind() -- Hangi Port Üzerindeyim?
Bir soket edindikten sonra bunu makinanızdaki bir "port" ile ilişkilendirmek isteyeceksiniz[138]. Bu port numarası dediğimiz şey işletim sistemi çekirdeği tarafından gelen bir paketi belli bir sürecin soket tanımlayıcısı ile ilişkilendirebilmesi için gereklidir. Eğer tek yapacağınız bir yere connect() ile bağlanmaksa o zaman buna gerek yoktur tabii. Gene de okumaya devam edin, zevk alacaksınız.
bind() sistem çağrısının özetine man komutu ile bakacak olursanız şöyle bir şeyle karşılaşırsınız:
#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *my_addr, int addrlen); 
sockfd denilen şey socket() tarafından döndürülen soket dosya tanımlayıcısıdır. my_addr değişkeni struct sockaddr türünde bir veriye işaret eder ve bu yapı da adresinizi yani port numaranızı ve IP adresinizi barındırır. addrlen'in değeri sizeof(struct sockaddr) olarak verilebilir.
Vay canına. Bir seferde sindirmek için biraz fazla değil mi? Bir örnek yapalım:
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MYPORT 3490

main()
{
    int sockfd;
    struct sockaddr_in my_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0); // hata kontrolünü ihmal etmeyin!

    my_addr.sin_family = AF_INET;         // konak bayt sıralaması
    my_addr.sin_port = htons(MYPORT);     // short, ağ bayt sıralaması
    my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
    memset(&(my_addr.sin_zero), '\0', 8); // yapının geriye kalanını sıfırlayalım

    // bind() için hata kontrolü yapmayı unutmayın:
    bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
    .
    .
    . 
Burada dikkat etmeniz gereken bazı şeyler var: my_addr.sin_port Ağ Byte Sıralamasında, ve tabii my_addr.sin_addr.s_addr de öyle. Bir başka nokta ise en tepede çağrılmış olan başlık dosyaları sistemden sisteme değişiklik gösterebilir. Kesin bilgi sahibi olabilmek için sisteminizdeki man sayfalarına bakmalısınız.
bind() konusu ile ilgili son söyleyeceğim şey IP ve port edinme işlemini biraz otomaktikleştirilebilir:
my_addr.sin_port = 0; // kullanılmayan herhangi bir port'u seç
my_addr.sin_addr.s_addr = INADDR_ANY;  // IP adresimi kullan 
Gördüğünüz gibi my_addr.sin_port değişkeninin değerini sıfır yaparak bind() işlevine diyoruz ki "uygun olan bir port sayısını bizim için sen seç". Benzer şekilde my_addr.sin_addr.s_addr değişkeninin değerini INADDR_ANY yaparak üzerinde çalıştığı makinanın IP adresini almasını söylemiş oluyoruz.
Eğer dikkatli bir okuyucu iseniz bazı şeyleri fark etmiş olabilirsiniz, mesela INADDR_ANY değerini Ağ Bayt Sıralamasına dönüştürmedim! Ne kadar da yaramaz bir programcıyım! Ama bildiğim bir şey var: INADDR_ANY zaten SIFIR değerine karşılık geliyor! Yani hangi sırada dizerseniz dizin zaten sıfır. Ancak takıntılı olan programcılar INADDR_ANY sabitinin mesela 12 olarak belirlendiği bir paralel evrenden söz açabilirler ve orada benim kodum çalışmaz. Tamam, sorun değil, hallederiz:
my_addr.sin_port = htons(0); // kullanılmayan herhangi bir port'u seç
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // IP adresimi kullan 
Sistemimiz artık o kadar taşınabilir oldu ki yani bu kadar olur. Bunu burada belirttim çünkü karşılaşacağınız kod örneklerinin çoğu INADDR_ANY sabitini htonl() işlevinden geçirmez, kafanız karışmasın.
bind() eğer bir hata çıkarsa -1 değerini döndürür ve errno isimli hata kodu değişkenine gerekli sayıyı yerleştir.
bind() işlevini çağırırken dikkat etmeniz gereken bir başka şey de şudur: port numarası olarak küçük bir değer seçmeyin. 1024'ün altındaki tüm portlar REZERVE edilmiştir (eğer superuser değilseniz!). Bu sayıdan başlayarak 65535'e kadar olan sayılardan birini port numarası olarak kullanabilirsiniz (tabii eğer seçtiğiniz numara başka bir program tarafından kullanılmıyorsa).
Bazen bir sunucuyu tekrar çalıştırmaya kalktığınızda bind() işlevinin başarısız olduğunu ve "Adres kullanımda" ("Address already in use.") mesajı verdiğiniz görürsünüz. Bu ne anlama gelir? Daha önce kullanılmış bir soket hala çekirdek seviyesinde takılı kalmıştır ve bu yüzden portun kullanılmasını engellemektedir. Ya kendiliğinden iptal olmasını beklersiniz ki bu 1 dakika kadar sürebilir ya da programınıza bu portu her halükârda kullanmasını sağlayacak kodu eklersiniz:
int yes=1;
//char yes='1'; // Solaris programcıları bunu kullanır

// "Address already in use" hata mesajından kurtul
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
    perror("setsockopt");
    exit(1);
} 
bind() ile ilgili ek bilgi
Kimi durumlarda bu işlev ile hiç işiniz olmaz. Mesela eğer connect() ile uzaktaki bir makinaya bağlanıyor ve yerel portunuzun ne olduğu ile ilgilenmiyorsanız (telnet uygulamasında olduğu gibi önemli olan sadece uzaktaki makinadaki port ise) kısaca connect() işlevini çağırırsınız ve zaten bu işlev de soketin bağlı olup olmadığını kontrol eder ve eğer soket bağlı değil ise bind() işlevini kullanarak bunu makinanızdaki kullanılmayan bir port numarasına bağlar.


[138] Yani eğer listen() kullanacaksanız genellikle böyle bir şey yapmanız beklenir zaten. Mesela bir oyun sunucusuna "telnet x.y.z port 6969" şeklinde bağlanmanız söylendiğinde karşı tarafta tam da böyle bir hazırlık yapılmıştır.
Önceki Üst Ana Başlık Sonraki
socket() -- Al Şu Dosya Tanımlayıcıyı! Başlangıç connect()--Hey, sen!
Bir Linux Kitaplığı Sayfası