Blog

Unix Ağ Bağlantısı ve Sistem Çağrıları

Merhaba,

Bu blogta soket programlama için bilinmesi gereken ileri seviye ağ bilgisi yani soket yapısı ve sistem çağrılarının çalışma mekanizmalarını inceleyeceğiz. Blog boyunca unix sistemlerin ağ bağlantı mimarisi ve ileri seviye sistem çağrılarının ne olduğu, çağrıların çalışma prensipleri ve mesaj iletilerinin içerikleri detaylı olarak ele alınacaktır.  Okumayı bitirdikten sonra yaptığınız geri bildirimler benim için çok önemlidir. Yorum olarak yada mesaj olarak bana dönüş yapabilirsiniz yada sorularınızı sorabilirsiniz. Unutmayın bilgi bilginin köprüsüdür ve bilgi paylaştıkça çoğalır.


Soket Nedir?

Soket aslında herkesin bildiği veya farkında olmadan kullandığı bir yapıdır. Soket dediğimiz şeyi eski telefon sistemlerine (*Epey eski) benzetebiliriz. Eskiler daha iyi bilir geçmişte telefon bağlantıları doğrudan kurulamazdı. A kişisi B kişisini aramak istediği zaman santral üzerinden B kişine bağlantı yapmak zorundaydı. Santral olarak ifade edilen yer ise bir nevi yönlendirici işini yapmaktaydı. Siz görüşmek istediğiniz kişinin bilgisini verirsiniz ve santral sizin bağlantınızı doğrudan görüşmek istediğiniz kişinin bağlantısına bağlar. Bu şekilde görüşmek istediğiniz kişiyle bir tünel açmış olursunuz. Soket genellikle bu yapıyla örneklendirilmektedir.

Soket temel olarak TCP/IP veri iletimi sırasında var olması gereken iki bilginin yan yana yazılarak bir araya getirilmesiyle oluşan bir iletim tünelidir. Bu iki bilgi IP bilgisi ve port numarasıdır. Örnek olarak 192.168.1.7 IP adresinin 80 numaralı portuyla görüşmek istiyorsak kullanacağımız soket yapısı şu şekilde olacaktır:

192.168.1.7:80

Burada gördüğünüz gibi IP adresi ve port numarası belirtilmektedir. Doğrudan nokta atışı yapılarak veri hedefe gönderilmektedir. İnternet veri iletişiminde bağlantı açmak olarak bilinen ifade aslında bir soketin açılmasıdır. Unix sistemlerde bu tür bağlantıların sağlanmasıyla TCP ve UDP protokol özelliiklerine uygun olarak veri iletimini yapılması için bağlantı kurmak amacıyla kullanılan bazı sistem çağrıları vardır. Bu makalenin amacı unix sistemlerdeki ağ bağlantılarının kurulmasını sağlayan sistem çağrılarını açıklamaktır. Açıkçası konumuz soket programlama ve ağ programlama diyebiliriz. Burada soket programlamada kullanılan kodlar anlatılmayacaktır. Bir internet bağlantısının kurulması sırasında yerel makinede kim kime seslenir kim ne yapar bunları öğreneceğiz. Giriş kısmını fazla uzatmadan linuxta her şey bir dosyadır sözüyle işe koyulalım. Bu söz ne demek istiyor, bunu söyleyenler ne kadar ciddi birazdan göreceğiz.


Socket() [I bless you !]

Socket() işlevi ağ iletişimi için bir uç nokta oluşturulmasını sağlar. Daha sonra oluşturulan bu uç noktayı ifade eden bir dosya tanımalayıcı döndürür. Başarılı bir çağrı tarafından döndürülen dosya tanımlayıcı, o anda herhangi bir işlem için açık olamyan en küçük numaralı dosya tanımlayıcı olacaktır.

Sözdizimi

#include <sys/types.h>
#include <sys/socket.h>

int socket (int domain, int type, int protocol);

Genel olarak adres ailesi tanımının yapılması için domain argümanına AF_INET değeri verilmektedir. Type argümanına verilen değer ile çekirdeğe ne tür bir soket söz konusu olacağı hakkında bilgi verilmektedir. Bunlar genel olarak en çok kullanılan argümanlardır. Tabikide bunlardan çok daha fazla (Hatta baya fazla) çeşitlilikte domain ve type argüman değerleri vardır. Man sayfasından tüm argüman değerlerine erişilebilir.

Socket() işlevi bir soket tanımlayıcısı döndürürmektedir ve bu aşamayı tamamladıktan sonra artık sonraki işlevleri parametreler olarak ilerletebilmekteyiz. Soket tanımlayıcının döndürülmesi sırasında bir hata ile karşı karşıya kalınırsa socket() işlevi -1 değeri döndürmektedir. -1 değerinin döndürülmesiyle errno isimli bir evrensel (global) değişkene hata kodu atanır. Errno değişkenine atanan hata kodları hakkında çok detaylı bilgi yine socket() işlevinin man sayfasında tüm detaylarıyla birlikte yer almaktadır.

Not:

AF_INET : Address Family

PF_INET : Protocol Family

Teorik olarak AF_INET ifadesini struct sockaddr_in yapınsında kullanabileceğimiz ve PF_INET ifadesini de socket() işlevini çağırırken kullanabileceğimiz belirtilemektedir. Aslına bakılırsa bir çok soket programcısının görüşüde bu yöndedir. Fakat pratik olarak uygulamalarda AF_INET ifadesini tüm yapılarda kullanabilmekteyiz. Pratik kullanım açısından AF_INET kullanımını destekleyenlerden biriside Richard Stevens’tır. Eğer inceleme fırsatınız olursa bir çok örneğinde AF_INET ifadesine yer verdiğini ve tüm yapılarda bu ifadeyi sürekli olarak kullandığını görebilirsiniz.


Bind() [Who is available?]

Socket() işlevi kullanılarak bir soket edindikten sonra bu soketin makine üzerinde bir port ile ilişkilendirilmesi gereklidir. Bu port numarası işletim sistemi çekirdeği tarafından gelen bir paketi belirli bir sürecin soket tanımlayıcısı ile ilişkilendirilmesi açısından gereklidir. Amaç sadece bir uzak bağlantı kurmak ise port ilişkilendirilmesine gerek yoktu.

Sözdizimi

#include<sys/types.h>

#include<sys/socket.h>

int bind(int sockfd, struct sockaddr *bizimadresimiz, int addrlen);

şeklinde bind sistem çağrısının özetini ifade edebiliriz. Bu yapıyı ve çok daha fazla detayıyla birlikte öğrenmek isterseniz bind() sistem çağırısnını man dosyasını inceleyebilirsiniz.

Burada sockfd olarak ifade edilen argüman socket() işlevi tarafından döndürülen soket dosya tanımlayıcısıdır.(Hatırlamakta fayda var ! Linuxta her şey dosyadır cümlesini mutlaka duymuşsunuzdur !)

Struct tipinde belirtilen bizimadresimiz argümanı da struct sockaddr türünde bir veridir. Bu yapı içerisinde makinemizin sahip olduğu IP adresi ve Port numarası yer almaktadır.

Burada tekrar belirtmekte fayda socket() işlevinde olduğu gibi bind() sistemde çağrısında da eğer bir hata ile karşı karşıya kalınırsa -1 değeri döndürülmektedir. Ayrıca bind() işlevi hatayla karşılaşılması durumunda errno adlı değişkene ilgili hata kodunun atamasını gerçekleştirir.

Dikkat !

Bind() sistem çağırısın kullanımı sırasında önemli olan ve özellikle dikkat edilmesi gereken hususlardan biriside port seçiminin doğru ve dikkatli bir şekilde yapılmasıdır. Çünkü soket tanımlayıcısı ve port ilişkilendirilmesi esnasında programcı tarafından belirtilen port numarası başka bir servis tarafından kullanılıyorsa hata ile karşılaşılması muhtemeldir. Ayrıca port seçimi sırasında 1024’ün altındaki port numaralarından seçim yapılmaması büyük bir karışıklığın önüne geçilmesinde faydalı olacaktır. Çünkü 1024’ün altındaki port numaraları “rezerved ports” yani rezerse edilmiş standart portlardır.Örneğin ssh, telnet, ftp gibi servislerin portları sabittir. Bir sistemde superuser olmadığınız sürece bu port numaraları üzerinde değişiklik yapamazsınız !

Bu önemli uyarıyı dikkate aldıktan sonra 1024 ile 65535 arasında bir port numarası seçildiği halde “Address Halihazırda Kullanımda” şeklinde bir uyarı alınırsa bu daha önceki kullanımlardan bir soketin çekirdek seviyesinde takılı olarak kaldığını göstermektedir. Bu hata çok uzun süreli veya kalıcı bir hata olmayacaktır. Yaklaşık olarak 1-2 dakika içerisinde port kapatılacak ve kullanıma hazır hale gelecektir. Fakat 1-2 dakika gibi bir süre aslında kısa bir süre değildir. Kritik iletişimler yada uygulamalar açısında büyük bir time out’u ifade etmektedir. Bunun için soket programcısının belirttiği portu her şart altında kullanabilmesi için program kodları arasında bunu belirtmesi gerekmektedir.

int yes=1;
if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
    perror("setsockopt");
    exit(1);
} 

Bu kod parçası sayesinde sizeof(int) işlevinin sonucu -1 olduğu durumlarda; yani bind() işlevinin çalışması esnasında bir hatayla karşılaşılmış ise bu durumda yine bağlantının belirtilen port kullanılarak yapılması sağlanmış olur. Bazı durumlarda kaynak port bilgisi çok önem arz etmemektedir. Bu durumlarda hedef port daha önemli bir yere sahiptir. Örneğin TELNET uygulaması ile gerçekleştirilen bir bağlantı sırasında önemli olan uzaktaki makinenin port numarasıdır. Bu durumda kısaca connect() işlevi çağrılır. Connect() işlevi zaten bir port üzerinden bağlı olunup olunmadığını kontrol eder ve bağlı değilse bind() işlevini otomatize olarak çağırarak bu soketin makinede kullanılmayan random bir porta bağlayacaktır.


Connect()  [A storm is brewing !]

Connect() işlevi socket() sistem çağrısı ile bir soket tanımlayıcısı edindikten sonra uzak bir makineye doğrudan bağlanmak için çağrılmaktadır.

Söz Dizimi

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, sruct sockaddr *serv_addr, int addrlen);

Burada sockfd olarak belirttiğimiz değişken bind() işlevinde olduğu gibi socket() sistem çağırıs tarafından döndürülmüş olan socket dosya tanımlayıcısının değerini tutmaktadır. serv_addr değişkeni ise bind() işlevinde kendi makinemizin IP adresini ve Port numarasını tutulduğu gibi connect() işlevindede serv_addr hedef makinenin Ip adresini ve Port numarasını tutmaktadır.

Socket() işlevinde ve Bind() işlevinde olduğu gibi connect() sistem çağrısı esnasında bir hata ile karşı karşıya kalınması durumda -1 değeri döndürülecek ve errno değişkenine karşılaşılan hata ile ilgili bir hata kodu atanacaktır. Connect() çağrısı kullanıldığı zaman bind() çağrısı kullanılmamaktadır. Buda demek oluyor ki connect() çağrıları sırasında özellikle kaynak adres belirtimi yapılmamaktadır. Zaten yukarıdaki belirtilen söz diziminde görüldüğü gibi kaynak adresle ilgili herhangi bir argüman bulunmamaktadır. Connect () işlevinin söz diziminde yalnızca hedef adres ile ilgilenilmektedir. Hedef adres olarak belirtilen serv_addr değişkeni içerisinde IP adresinin ve Hedef Port Numarasının yer aldığını tekrar hatırlatmakta fayda var !

Bind() işlevinin kullanılmaması durumunda biliyoruz ki sistem çekirdeği otomatik olarak 1024 ile 65535 arasında kullanıma müsait olan rastgele bir port belirleyecek ve bağlantıyı bu port üzerinden yapacaktır. Hedef makine ise bu port değerini otomatik olarak bizde öğrenecektir. Connect() çağrısında bu bilgilerin verilmesi gerekli değildir !


Listen()  [Who is speaking ?]

Eğer herhangi bir bağlantı isteği yapmak gibi bir isteğimiz yoksa ve sadece makinemizin adresine gelen bağlantıları sonrasında değerlendirmek üzere dinlemek istiyorsak listen() sistem çağrısını/işlevini kullanmaktayız.

Söz Dizimi

int listen(int sockfd, int backlog);

Bind() ve Connet() işlevlerinde belirttiğimiz gibi listen() sistem çağrısının söz dizimindede yer yer alan sockfd değişkeni socket() işlevi tarafından döndürülen soket dosya tanımlayıcısının değerini tutmaktadır. Burada diğer işlevlerden farklı olarak backlog değişkeni yer almaktadır. Bu da kullanılan işlevin bir bağlantı kurmak amacıyla değilde gelen bağlantıları dinlemek amacıyla kullanılmasından ortaya çıkan bir problemi gidermek için belirtilmektedir. Backlog değişkeni sayesinde makinemizin sahip olduğu adrese gelecek olan bağlantı ve bağlantı isteklerinin çağrı kuyruğunda izin verilen bağlantı sayısını ifade etmektedir. Çağrı kuyruğunu biraz daha detaylı incelyecek olursak şu şekilde açıklayabiliriz:

Makinemize gelen bağlantı isteklerini accept() işlevi sayesinde kabul edebiliriz. Sırasıyla gelen bağlantı isteklerini kabul ederken ilgilendiğimiz istek haricindeki istekler değerlendirilmek üzere sırada beklemektedirler. Bu çağrı kuyruğunda bekleyebilecek çağrı sayısı bir çok sistemde varsayılan olarak 20 değerini almaktadır. Bu değer soket programcısı tarafından isteğe bağlı olarak değiştirilebilir.

Socket(), Bind() ve Connet() sistem çağrılarında olduğu gibi Listen() sistem çağrısıda bir hata ile karşı karşıya kalırsa -1 değeri döndürürmekte ve errno adlı değişkende ilgili hata kodunu tutmaktadır.

Eğer belirli bir port üzerinden dinleme yapılacaksa öncesinde mutlaka bind() işlevi kullanılması gereklidir. Çünkü soket dosya tanımlayıcısı ile port ilişkilendirilmesini gerçekleştiren işlev bind() işlevidir.

Basitçe sırasıyla gelen bağlantıları dinlemek ve kabul etmek için aşağıdaki temsili gösterim sırasıyla kullanılmaktadır.

Socket();

bind();

listen();

accept(); # Bağlantının kabul edilme hususu çok önemli !

.

.

.


accept()  [ Yeap !]

Accept() sistem çağrısı diğer çağrılara nispeten biraz daha farklılık göstermektedir. Bu işlev sayesinde listen() işlevi ile dinlemekte olduğumuz bir porta uzak bir makineden connect() işlevi ile bir bağlantı talebi çağrısı gelirse bunu kabul edebiliriz. Bildiğimiz üzere listen() işlevi sayesinde dinlemekte olduğumuz port üzerinden yerel makinemize gelen bağlantı isteklerini belirli bir sıraya koyuyorduk. Hatta bu çağrı kuyruğunda bekleyebilecek bağlantı talebi sayısını spesifik olarak belirleyebiliyorduk. Evet işte bu kuyrukta bekleyen ve sırası gelen bağlantı çağrısını kabul etmek için accept() işlevini kullanmaktayız. Accept() işlevi bind(), connect() ve listen() işlevlerinden farklı olarak socket() işlevi gibi bir soket tanımlayıcısı değeri döndürmektedir. Bu söz konusu soket tanımlayıcısı yalnızca ilgili bağlantı için geçerlidir. Yeni oluşturulan soket tanımlayıcısı sadece bağlantı çağrısı kabul edilen makine için gönderim ve alım işlemlerinde kullanılmak üzere ayrılmıştır. Bu sırada socket() işlevi ile oluşturulan ve listen() işlevi ile dinleme yapılan önceki soket tanımlayıcısı aynı işlevine devam etmektedir yani listen() işlevi sayesinde bind() işlevi ile ilişkilendirilen port üzerinden dinlemeye devam etmektedir. Yeni gelen bağlantı çağrılarının değerlendirmeleri devam etmektedir. Kabul edilen her bir bağlantı için accept() işlevi yeni bir soket tanımlayıcısı üretmektedir.

Söz Dizimi

#include <sys/socket.h>

int accept (int sockfd, void *addr, int *addrlen);

Aceept() işlevinin özetinde yer alan sockfd değişkeni önceki işlevlerden bildiğimiz üzere listen() ile dinlediğimiz soket tanımlayıcısını ifade etmektedir. Addr değişkeni ise struct sockaddr_in yapısını gösteren işaretçidir. (Pointer). Addr değişkeninin işaret etmiş olduğu struct sockaddr_in yapısı içerisinde gelen bağlantıyla ilgili bilgiler yer almaktadır. Kısacası gelen bağlantının hangi makineden, hangi IP adresi ve Port Numarası ile geldiğini bu yapı içerisinde öğrenebilmekteyiz. Addrlen değişkeni ile belirtilen değer ise addr değişkeninin içerisine yerleştirilecek maksimum byte değerini ifade etmektedir. Addrlen değerinden daha fazlası addr değişkenine yerleştirilemez. Eğer addr değişkenine addrlen değerinden daha az byte yerleştirilmiş ise addrlen değeri, addr değişkenine atanan byte değerinin boyutuna göre güncellenecektir.

Accept() işlevinden önceki işlevlerde olduğu gibi accept() çağrısıda herhangi bir hata ile karşı karşıya kalırsa bu durumda -1 değeri döndürmektedir. Ayrıca errno adlı değişkene ilgili hata kodunu atamaktadır.

Not: Eğer tek bir bağlantı kabul edilecekse bu durumda close() işlevi ile listen() işlevi tarafından oluşturulan sockfd üzerindeki dinleyici kapatılmalıdır. Böylece dinlenilmekte olan aynı port üzerinden başka bağlantı yapılamayacaktır.


Send() ve recv() [if TCP:  echo <RFC 793>] [Let’s talk but be carefull]

Send() ve recv() işlevleri veri akış soketleri (SOCK_STREAM) veya veri paketi soketi (SOCK_DGRAM) üzerinden veri gönderilmesini send() ve veri alınabilmesini recv() sağlamaktadır. Bu iki işlev için bağlantı oluşturulması mutlaka gerekmektedir.

Send İşlevinin Söz Dizimi

int send (int sockfd, const void *mesaj, int len, int flags);

Bu söz diziminde yer alan:

sockfd değişkeni üzerinden veri gönderilecek olan soket dosya tanımlayıcısıdır.

*mesaj değişkeni gönderilmek istenen mesajı işaret eden işaretçi niteliğindedir.

Len değişkeni mesaj işaretçisi tarafından gösterilen verinin byte cinsinden boyutudur.

Flags değişkeni ise genel olarak “0” değerini almaktadır. Bunun yanısıra bitsel (bitwise) değer alabilir. Flags argümanının alabileceği diğer değerler:

  • MSG_CONFIRM

  • MSG_DONTROUTE

  • MSG_DONTWAIT

  • MSG_EOR

  • MSG_MORE

  • MSG_NOSIGNAL

  • MSG_OOB

Send() işlevi değer olarak gönderilen byte miktarını döndürmektedir. Send() işlevi ile gönderilmek istenen mesaj boyutu ile send() işlevinin göndermiş olduğu veri boyutu her zaman eşit olmayabilir. Yani send() işlevi istenilen miktardan daha az boyutta veri gönderiş olabilir. Daha doğrusu ancak o kadarını göndermeyi başarmış olabilir. Bu durumda gönderilmesi istenilen fakat gönderilmemiş veri kalabilmektedir. Send() işlevi veri aktarımında bir eksiklik olduğu zaman gönderici eksik gönderimi denetlyebilsin diye gönderilebilinen toplam veri boyutunu döndürürmektedir.

Önceki işlevler olduğu send() işlevi herhangi bir hata ile karşı karşıya kaldığı zaman -1 değeri döndürür ve errno adlı değişkene söz konusu hata ile ilgili hata kodunun atamasını yapar.

Recv() işlevinin söz dizimi

int recv ( int sockfd, void *buffer, int len, unsigned int flags)

send() işlevinde veri göndermek için kullanılan sockfd argümanı recv() işlevinde veri okuma işlemininin gerçekleştirileceği soket dosya tanımlayıcısı ifade etmektedir. Buffer verinin yazılacağı bellek başlangıç adresini ifade eden işaretçidir (pointer). Len argümanı verinin yazılacağı maksimum tampon (buffer) boyutudur. Flags argümanı genel olarak 0 değerin alabilmektedir. Flag argümanının alabileceği diğer değeler şunlardır:

  • MSG_CMSG_CLOEXEC

  • MSG_DONTWAIT

  • MSG_ERRQUEUE

  • MSG_OOB

  • MSG_PEEK

  • MSG_TRUNC

  • MSG_WAITALL

recv() işlevi send() işlevinde olduğu gibi okuma işlemi yapıldıktan sonra tampona yazılan byte miktarını döndürmektedir. Diğer işlevler olduğu gibi recv() işlevinin çağrılması ve çalışması sırasında herhangi bir hata ile karşı karşıya kalınırsa -1 değeri döndürülmektedir. Ayrıca errno adlı değişkene ilgili hata hakkında hata kodu atanmaktadır. Ayrıca recv() işlevi 0 değeri de döndürebilmektedir. 0 değerinini döndürülmesi uzak makinen bağlantıyı sonlandırdığı kestiği anlamına gelmektedir.

Detaylı inceleme için man sayfasını okuyabilirsiniz.


sendto() ve recvfrom [if UDP: echo <RFC 768>]  [Let’s talk!  Keep your cool. ]

Bu iki işlev send() ve recv() işlevleriyle ortak amaca hizmet etmektedirler. Fakat aralarında önemli bir fark vardır. Aslında bu fark kısaca TCP ve UDP protokolleri arasındaki farkın ta kendisidir. Kısaca mesajların bağlantısız veri paketi soketleri üzerinden gönderilmesi anlamına gelmektedir. Bunun anlamıda UDP soketlerinin kullanımı demektir. UDP soketleri kullanılırken başlangıç olarak ilk paketi göndermeden önce hedef adresin belirtilmesi gerekmektedir.

Sendto() Söz Dizimi

int sendto (int sockfd, const void *mesaj, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Sendto() işlevi özeti itibariylede send() işlevine benzerlik göstermektedir. Fakat yukarıdaki özetleri karşılaştırdığımızda sendto() işlevi send() işlevinden fazladan 2 argüman daha almaktadır. To olarak belirtilen değişken aslında bildiğimiz struct sockaddr türündeki değişkeni işaret etmekte olan bir işaretçidir. Biliyoruzki struct scoktaddr_in değerin hedef IP adresini ve Port numarasını içermektedir.tolen değişkeni ise sizeof(struct sockaddr) değerini almaktadır.

Sendto() işlevide send() işlevinde olduğu gibi sonuç olarak gönderilen byte miktarını döndürmektedir. Bu aşamada belirtilen byte miktarından daha az miktarda byte gönderilmiş olma ihtimali vardır. Bu durumda gönderilmemiş veri olduğunun tespit edilmesi açısında gönderimi başarılı olan toplam byte değeri göndürülmektedir.

Ayrıca socket(), bind(), listen(), connect(), send() ve recv() işlevlerinde olduğu gibi sendto() işlevinin çağrılması ve işleyişi esnasında herhangi bir hata ile karşı karıya kalınması durumunda -1 değeri döndürülmekte ve errno adlı değişkene söz konusu hata ile ilgili olan hata kodu atanmaktadır.

Recvfrom() Söz Dizimi Özeti

int recvfrom (int sockfd, void *buffer, int len, unsignet int flags, struct sockkaddr *from, int *fromlen);

Bu özette görüldüğü recvfrom() işlevi recv() işleviyle benzerlik göstermektedir. Burada fazladan bir kaç ek değişken yer almaktadır. From yerel olarak struct sockaddr türünde olan ve içerisinde mesajın geldiği makinenin adres bilgisi ile port numarasını bulunduran bir yapıyı işaret etmektedir.

From len yerel ve integer türünde bir değerdir. İlk olarak sizeof(struct sockaddr) değerini almalıdır. Recvfrom değişkeni çalıştırılıp bir değer döndürmesi sağlandığında fromlen değişkeni from değişkenindeki adresinin boyutunu depolayacaktır.

Recv() işlevinded olduğu gibi recvfrom() işlevide okumuş olduğu byte miktarının değerini döndürecektir ve herhangi bir hata ile karşı karşıya kalması durumunda yine -1 değeri döndürecektir. Ayrıca errno değişkenine ilgili hata kodunu atayacaktır.

Not: Connect() ile bir UDP soketine bağlantı kurulduğunda send() ve recv() işlevleride kullanılabilmektedir. Soket hala bir bağlantısız veri soketi olarak kalacak ve gönderilip-alınan paketler UDP protokolünü kullanmaya devam edeceklerdir. Bu kullanımda soket arayüzü otomatize olarak hedef ve kaynak bilgilerini paketlere ekleyecektir.


Close () ve shutdown() [someone gonna death 🙂 ..]

Close() ve Shutdown() işlevleri soket dosya tanımlayıcısı ile ilişkilendirilmiş olan bağlantıyı kesmek, sonlandırmak için kullanılmaktadır. Close () işlevi unix’teki normal dosya tanımlayıcı kapatma işlevi olarak bilinen işlevdir. Soket dosya tanımlayıcısının iki yönlü olarak tamamen kapatılmasını sağlamaktadır. Eğer alıcı taraf close() işlevi ile bağlantıyı sonlandırırsa gönderici taraf iletişim kurmaya çalıştığında hata mesajı ile karşılacak ve bir mesaj döndürülecektir. Close () işlevi herhangi bir denetim yapılmasını sağlamamaktadır. Soket tanımlayıcı kapatma işlemlerinde bağlantı denetimli olarak sonlandırılacak ise shutdown () işlevi kullanılmaktadır.

Close () Söz Dizimi

close (sockfd)

Shutdown () işlevi sayesinde kapatma işlemi esnasında farklı denetim haklarına sahip olunmaktadır. Bu işlev sayesinde iletişim kanalı tek yönlü yada çift yönlü olarak kapatılabilir. Fakat close () işlevinde sadece iki yönlü olarak doğrudan soket kapatma işlemi gerçekleştirilir.

Shutdown() Söz Dizimi

int shutdown (int sockfd, int how)

Burada belirtilen sockfd değişkeni diğer işlevlerde olduğu soket dosya tanımlayıcısı ifade etmektedir. Shutdown () işlevi için sockfd değeri kapatılmak istenen soket dosya tanımlayıcısını ifade etmektedir.

How değişkeninin alabileceği değerler ve anlamları:

  • 0 : Sonraki okumalara izin verme !

  • 1 : Sonraki göndermelere (yazmalara) izin verme !

  • 2 : Sonraki göndermelere ve okumalara izin verme ! (Tamamiyle 2 yönlü kapatma- close() gibi.)

Shutdown( ) işlevi başarıyla tamamlanırsa 0 değeri döndürmektedir. Fakat shutdown işlevinin çağrılması ve çalışması esnasında herhangi bir hata ile karşı karşıya kalınırsa -1 değeri döndürülmektedir. Ayrıca diğer tüm işlevlerde olduğu gibi errno değişkenine ilgili hata kodu atanmaktadır. Shutdown() işlevi UDP veri soketlerinde yani bağlantısız veri paketi soketleri üzerinde kullanıldığı zaman bu soketler send() ve recv() işlevleri tarafından kullanılamaz hale gelirler.

Not: Eğer connect() ile send() ve recv() işlevleri kullanılmak istenirse bu gerçekleştirilebilir.

Shutdown() ve close() işlevleri arasındaki en önemli ayırt edici kısım shutdown() işlevinin dosya tanımlayıcısını aslında kapatmayıp, yalnızca bu dosya tanımlayıcısının kullanılabilirliğini değiştirirken close() işlevinin soket dosya tanımlayıcısını gerçekten tamamen iptal etmesidir.


Getpeername( ) [Hey who are you ? ]

Bu işlem bağlantılı veri akış soketinin yani SOCK_STREAM soketinin diğer tarafında kimin olduğunu söylemektedir.

Söz Dizimi

#include <sys/socket.h>

int getpeername (int sockfd, struct sockaddr *addr, int *addrlen);

Burada belirtilen değişkenler diğer işlevlerde kullanılan argümanlarla aynıdır. Sockfd argümanı bağlantılı veri akış soketinin dosya tanımlayıcısıdır. Addr struct sockaddr (struct sockaddr_in) türündeki bir işaretçidir (pointer). Struct sockaddr_in yapısı içerisinde iletişimin diğer ucundaki makinenin adres bilgileri Ip adresi ve Port numarası olarak tutulmaktadır. Addrlen, integer türünde bir işaretçi yani pointerdır. Bu argümanın alması gereken ilk değer sizeof(struct sockaddr) olarak ifade edilmektedir.

Getpeername () işlevi diğer işlevlerde olduğu gibi çağrılması ve çalışması esnasında herhangi bir hata ile karşı karşıya kalırsa -1 değeri döndürmektedir. Ayrıca errno adlı bir değişkene ilgili hata kodunu atamaktadır.


Gethostname( ) – [W..! Where am i ? & Who am i ?]

Getpeername( ) işlevi bağlantının diğer ucundaki makineye ait bilgilerin edinilmesini sağlarken gethostname( ) işlevide programın çalıştığı yani yerel makinenin bilgilerini ( ismini, Ip adresini) döndürmektedir.

#include <unistd.h>

int gethostname (char *hostname, size_t size);

Burada kullanılan argümanlar sırasıyla hostname ve size değişkenleri olarak ifade edilmektedir. Hostname( ) işlevin çağrılmasından sonra yerel makinenin ismini barındıracak olan karakter dizisinin işaretçidir yani pointerıdır. Size argümanı ise hostname dizisinin byte türünden uzunluğudur.

Gethostname( ) işlevi başarılı bir şekilde çalışır ve tamamlanırsa 0 değeri döndürmektedir. Herhangi bir hata ile karşı karşıya kalınması durumunda diğer işlevlerde olduğu gibi -1 değeri döndürmekte ve ayrıca errno adlı değişkene ilgili hata kodunu atamaktadır.


Umarım faydalı olmuştur. Okuduğunuz için teşekkür ederim.

Sağlıklı iyi günler dilerim.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir