B.5.1. Birinci Örnek—unix Yuvaları
Bu altkısımda verilen örnek programda ağ girdi/çıktısı için kullanılan
kütüphane yordamları sadece unix-benzeri sistemlerde kullanılabilir.
unix-benzeri sistemlerde kullanılan kütüphane fonksiyonları ve
sistem çağrılarının çoğu unistd.h
başlık dosyasının programa
#include
edilmesini gerektirir. Bazı çağrılar başka bazı özel
başlık dosyalarını (örneğin, sys/types.h
) gerektirebilir.
Daha ayrıntılı bilgi için unix programlama el kitaplarına
veya unix man
komutuna (örneğin,
man 2 socket
) başvurulabilir.
#include <stdio.h> /* perror, ...printf */ #include <stdlib.h> /* exit, EXIT_SUCCESS, EXIT_FAILURE */ #include <string.h> /* str..., mem... */ #include <signal.h> /* signal */ #include <time.h> /* time, localtime, strftime */ #include <unistd.h> /* gethostname, close */ #include <netdb.h> /* gethostbyname, struct hostent */ #include <sys/param.h> /* MAXHOSTNAMELEN */ #include <sys/types.h> #include <sys/socket.h> /* socket, bind, listen, accept */ #include <netinet/in.h> /* struct sockaddr_in, htons */ #include <arpa/inet.h> /* htons, inet_ntoa */ #define PROGISMI "dtp" #define KULLANIM ("Kullanim: " PROGISMI " [host_ismi [port_no]]\n") #define HATA (-1) char * tarih_saat (void) { static char cikti[30]; time_t z = time (NULL); struct tm * yzg = localtime (&z); strftime (cikti, sizeof(cikti)-1, "%Y-%m-%d %H:%M:%S %Z", yzg); return cikti; } /* tarih_saat */ int dinleme_icin_hazirlan (char * host, unsigned short port) /* Verilen port'u dinlemeye başla ve açılan soketi döndür. **/ { int s; struct hostent * hp; struct sockaddr_in sin = { 0 }; if (host[0]=='\0') { if (gethostname (host, MAXHOSTNAMELEN)) { perror ("gethostname"); exit (EXIT_FAILURE); } } hp = gethostbyname (host); /* Host ismini ağ numarasına çevir. */ if (hp == NULL) { fprintf (stderr, "%s: Host adresi bilinmiyor (%d).\n", host, h_errno); exit (EXIT_FAILURE); } s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); /* IPv4 */ if (s == HATA) { perror ("socket"); exit (EXIT_FAILURE); } sin.sin_port = htons (port); sin.sin_family = AF_INET; memcpy (&sin.sin_addr, hp->h_addr, hp->h_length); if (bind (s, (struct sockaddr *)&sin, sizeof(sin)) == HATA) { perror ("bind"); exit (EXIT_FAILURE); } if (listen (s, 15 /* backlog */) == HATA) { perror ("listen"); exit (EXIT_FAILURE); } fprintf (stderr, "%s - %s programi %s:%d uzerinde basladi.\n", tarih_saat(), PROGISMI, host, port); return s; } /* dinleme_icin_hazirlan */ void yanit_gonder (int s, struct in_addr ist_addr) { char * yanit = tarih_saat (); size_t yboy = strlen (yanit); send (s, yanit, yboy, 0); send (s, "\r\n", 2, 0); fprintf (stderr, "%s - [%s] - \"%s\" %zu\n", tarih_saat(), inet_ntoa(ist_addr), yanit, yboy); } /* yanit_gonder */ int sock; /* sinyal_yakala ve main tarafından kullanılıyor */ void sinyal_yakala (int sig) { signal(sig, SIG_IGN); fprintf (stderr, "%s - %s programi %d sinyali alarak sonlandi.\n", tarih_saat(), PROGISMI, sig); close (sock); exit (EXIT_SUCCESS); } /* sinyal_yakala */ int main (int argc, char **argv) { char hostismi [MAXHOSTNAMELEN] = ""; unsigned short port = 1313; /* varsayılan TCP dinleme portu */ int khata = 0; if (argc > 1 && sscanf(argv[1],"%s",hostismi) != 1) khata = 1; if (argc > 2 && sscanf(argv[2],"%hu",&port) != 1) khata = 1; if (argc > 3) khata = 1; if (khata) { fprintf (stderr, KULLANIM); return EXIT_FAILURE; } sock = dinleme_icin_hazirlan (hostismi, port); signal (SIGTERM, sinyal_yakala); /* kill için */ signal (SIGINT, sinyal_yakala); /* Ctrl+C için */ while (1) { struct sockaddr_in fsin; int ns; socklen_t flen = sizeof(fsin); ns = accept (sock, (struct sockaddr *)&fsin, &flen); if (ns == HATA) { perror ("accept"); close (sock); return EXIT_FAILURE; } yanit_gonder (ns, fsin.sin_addr); if (close (ns) == HATA) perror ("close"); } } /* main */
Yukarıda verilen program unix yuvaları
kullandığı için Standart C programı değildir. Bu örnekte ağ yuvalarını
işleyen standart-dışı fonksiyon ve başlık dosyaları kullanmaktayız.
Dikkat edeceğiniz üzere, programın başında #include
edilen
ilk beş başlık dosyası Standart C’ye aittir. Geri kalan başlık
dosyaları ise birçok POSIX ortamlarda (yani unix ve
Ek D’de göreceğimiz Cygwin ortamlarında)
standartlaşmış dosyalardır. Bu dosyaların hangi fonksiyon ve veri yapı
tanımları için kullanıldığı, #include
satırının sonunda, açıklama
kısmında verilmeye çalışılmıştır. Bu programı derlerken,
normalde kullandığımız -std=c17
komut satırı seçeneği
yerine -std=gnu17
seçeneğinin kullanılması önerilir.
Burada verilen programın işlevi tipik bir
Daytime Protokolü
ne
uygun davranan bir ağ servisine denk gelmektedir ve en basit ağ yazılımı
olarak değerlendirilebilir.
RFC 867’de
detayları verilen böyle bir servisin yaptığı şey çok basittir:
TCP tabanlı bir daytime servisi
13 nolu portu dinler. Bu porta bir
bağlantı isteği geldiğinde, bağlantı ile birlikte servise iletilen veriye
bakmaksızın, geriye günün tarih ve saatini bir ASCII karakter dizisi şeklinde
gönderir ve bağlantıyı hemen kapatır. Gönderilecek karakter dizisinin biçimi
belirtilmemiştir, ancak sistem tarih ve saatini içeren ve \r
(satırbaşı) ile \n
(satır ilerletme) karakterleri ile
sonlanan tek bir satırdan oluşması beklenir. İnternet üzerindeki
böyle bir servise bağlanmak için, İnternet erişimi olan bir sistemde,
örneğin telnet time.nist.gov 13
komutunu girebilirsiniz.
Yukarıdaki örnekte, varsayılan TCP dinleme portu olarak 13 yerine 1313
seçilmiştir. Bunun nedeni unix-benzeri sistemlerde böyle bir programı
normal
bir kullanıcı ile çalıştırdığınızda, 1024’ten düşük
portları dinleme
yetkisi olmayacağıdır. Program birinci argüman olarak
dinleme yapacağı host ismini veya IP adresini bekler; ikinci argüman olarak da
varsayılandan farklı bir TCP port numarası verilebilir. Bu argümanların
varsayılan değerleri, sistemin host isminden elde edilen yerel IP adresi ve
1313 TCP portudur. Bir sistemde birden fazla yerel IP adresi olabilir.
Bu program AF_INET
(yani IPv4) ağ etki alanını kullandığından,
en azından 127.0.0.1 adresini kullanabilirsiniz.
Programı yukarıda anlatıldığı gibi derledikten sonra, argümanlı veya
argümansız çalıştırabilirsiniz. Programın servisini test etmek için,
başka bir pencerede telnet localhost 1313
veya başka bir bilgisayarda
telnet servis_ip_addresi 1313
komutunu kullanabilirsiniz. Programı sonlandırmak için, çalıştığı
pencerede Ctrl+C veya başka bir pencerede
kill process_no
komutunu
girebilirsiniz.
Programın detaylı açıklaması bu ekin kapsamı dışındadır. Ancak, programı dikkatlice inceleyip anlamaya çalışmanızı öneririz. Özellikle, programı derleyebileceğiniz bir ortam (Linux veya Cygwin) temin edip denemeniz yararlı olacaktır.