4.2. sunucu.c
#include
#include
#include
#include
#include
#include
#define PORT 3333
#define LISTQUEUE 5
main(int argc, char *argv[]) {
int sockfd, new_fd;
struct sockaddr_in server_addr, client_addr;
int client_size;
char buffer[1024];
printf("%s %d portu uzerinde calismaya basladi...\n\n",argv[0],PORT);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
exit(1);
}
if(listen(sockfd, LISTQUEUE) != 0) {
perror("listen");
exit(1);
}
while(1) {
client_size = sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr*)&client_addr,&client_size)) ==
-1) {
perror("accept");
exit(1);
}
printf("%s sunucumuza baglandi...\n",inet_ntoa(client_addr.sin_addr));
memset(&buffer, 0, sizeof(buffer));
strcpy(buffer,"Merhaba ");
strcat(buffer,(char *) inet_ntoa(client_addr.sin_addr));
strcat(buffer," :)\n");
if(send(new_fd,&buffer, strlen(buffer), 0) == -1) {
perror("send");
exit(1);
}
if(recv(new_fd,&buffer, strlen(buffer)-1, 0) == -1) {
perror("recv");
exit(1);
}
printf("Alinan yanit: %s\n", buffer);
close(new_fd);
}
close(sockfd);
return 0;
}
Evet. İşte harika dünyaya adım attık ve "Merhaba" dedik. Bunu ilk denediğimde gözlerime
inanamamıştım. Başka bir bilgisayardaki kişiye merhaba demiştim. O zamanlar chat programları bu
kadar populer değildi. Henüz mirc yokken, irc'a müptela olduğumuz günler. Bu gerçekten harika bir
şey.
Şimdi programımızı linux altında "GNU C Compiler" gcc ile nasıl derleyeceğimizi görelim.
cclub:~> gcc -o sunucu1 sunucu.c
Bu komutla sunucu.c dosyasına yazdığımız kodları derleyip sunucu1 isimli çalışabilir dosyayı
ürettik. Bakalım neler oluyor:
cclub:~> ./sunucu1
./sunucu1 2222 portu uzerinde calismaya basladi...
193.140.168.54 sunucumuza baglandi...
Tahmin edeceğiniz gibi bunlar sunucunun ekranına yazanlar. "193.140.168.54 sunucumuza
baglandi..." mesajı, ceng hostundan bağlantı yapıldığında ekrana basılmıştır. Peki istemcinin
ekranına neler dönüyor:
ceng:~# telnet 193.140.168.77 2222
Trying 193.140.168.77...
Connected to 193.140.168.77.
Escape character is '^]'.
Merhaba 193.140.168.54 :)
Merhaba 193.140.168.54 :) mesajı, sunucu tarafından istemcimize (telnet) gönderilen mesajdır.
Onun üstündeki mesajları merak etmeyin. Onların bizimle ilgisi yok. Telnet'in ürettiği mesajlardır.
Windows makineden "Başlat->Çalıştır" kullanarak aynı komutu verebilirsiniz.
5. İstemci Soket Programı
Şimdi istemci bir soket programın genel yapısına bakalım. Başlangıç aşamasında işlemler sunucu
program ile aynı olacaktır. Ancak asıl iş yapılan kısımda hem mimari, hem de fonksiyonlar
değişeek. Çünkü bir taraf hizmet verme, öteki taraf ise hizmet alma durumunda olacaktır. Şimdi
geliştireceğimiz istemci yukardaki sunucu için geliştirilmemiştir. Bu programımız 3333 numaralı
portta çalışsın. Bu istemci program için bir de sunuu program yazdım. Sunucu programın
çalışmasını yukarıda anlattığımdan tekrar bu program için de anla. Bu programımız 3333 numaralı
portta çalışsın. Bu istemci program için bir de sunuu program yazdım. Sunucu programın
çalışmasını yukarıda anlattığımdan tekrar bu program için de anlatmayacağım. Doğrudan kodunu
vermekle yetineceğim.
Öncelikle port numaramızı belirleyelim:
#define PORT 3333
Sunucunun çalıştığı bilgisayar istemciye parametre olarak verilecek:
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Kullanimi : %s hostname ",argv[0]);
exit(1);
}
}
Sunucunun Belirlenmesi:
Parametre olarak alınan host ismini çözümlüyoruz. Adres yapısında olan server_addr değişkenine
PORT numarasını ve IP adresini yazıyoruz.
struct hostent *h_name;
if(argc > 1) {
h_name = gethostbyname(argv[1]);
}
else {
printf("Kullanimi: %s hostname ",argv[0]);
exit(1);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = *(u_long *) h_name->h_addr;
serv_addr.sin_port = htons(PORT);
printf("Sunucu adresi: %s ",inet_ntoa(serv_addr.sin_addr));
Sunucuya Bağlanma:
Artık hizmetkarımıza, yani sunucumuza bağlanıp selam verme vakti geldi.
printf("Mesajinizi girin: ");
fgets(buf, sizeof(buf)+1, stdin);
if((send(sockfd, buf, sizeof(buf), 0)) <= 0) perror("send");
if((recv(sockfd, buf, sizeof(buf), 0)) <= 0) perror("recv");
printf("Sunucu'dan gelen mesaj: %s", buf);
shutdown(sockfd, 2);
close(sockfd);
Aynen sunucu programda olduğu gibi soket açtık. Soket türümüz yine SOCK_STREAM. Sonra
connect()fonksiyonunu kullanarak açtığımız soket üzerinden bir bağlantı yaptık. connect()
fonksiyonu sys/types.h başlık dosyasında aşağıdaki gibi tanımlanmıştır:
int connect(int s, const void *addr, int addrlen);
İlk parametre soket tanımlayıcısı, ikinci parametre bağlantı kurulacak sunucunun adres bilgilerini
içeren server_addr yapısı, son parametre ise adres yapısının boyutudur. Artık sunucumuzla aramızda
stream bir soket bağlantısı kurmuş olduk. İstemci tarafında connect() yapıldığı zaman, sunucu
tarafında accept() yapılır. Yani yapılan bağlantı kabul edilir. Daha önce yazdığımız sunucu
programdaki accept() işlemi, telnet programının connect() isteğini karşılıyordu. Şimdi bizim
istemimiz bu isteği gönderecek. Dikkat ederseniz ilkel bir telnet programı yazıyoruz.
Sunucuya Bilgi Gönderme:
Sonraki kısmı, anlaşılması açısından basit tuttum. Ekrandan bir mesaj alıp bunu send() fonksiyonu
ile sunucuya gönderiyoruz. Bu fonksiyon, dosyalara yazmak için kullandığımız write() fonksiyonu
gibidir. send() ile sokete veri gönderiyoruz. Soket sunucu ile bağlantılı olduğundan veri, sunucuya
ulaşır. Sunucu ise her send() isteğine recv() ile karşılık verir. send() yada recv() işlemleri her iki
tarafta da yapılabilir. Sunucudan gelen her send() isteğine karşı da istemci de bir recv() vardır.
Örneğin ftp programında karşılık send() ve recv() işlemleri vardır. Siz önce istemci tarafından 'get
dosya' komutunu göndererek dosya isteğinizi belirtiyorsunuz. Sunucu bu komutu okuyup