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. |