8. TCP-IP Programlama
8.1. Genel Bilgiler
8.1.1. İnternet Protokolleri
Burada, iki ana protokolün adından esinlenerek TCP/IP (Transfer Kontrol Protokolü / İnternet Protokolü) olarak da adlandırılan İnternet iletişim protokollerine bir giriş sunuyoruz. Dağıtık uygulamaların geliştirilmesine geçmeden önce, okuyucunun ağların işleyişi ve özellikle TCP/IP protokollerine ilişkin genel bir kavrayışa sahip olması yararlı olacaktır.
Aşağıdaki metin, 90'lı yılların başında yayınlanan NOVELL'in "Lan Workplace for Dos - Administrator's Guide" adlı belgesinde yer alan bir metnin kısmi çevirisidir.
Farklı türdeki bilgisayarların bir ağını oluşturma konusundaki genel kavram, ABD'deki DARPA (Savunma İleri Araştırma Projeleri Ajansı) tarafından yürütülen araştırmalardan kaynaklanmaktadır. DARPA, heterojen makinelerin birbirleriyle iletişim kurmasını sağlayan ve TCP/IP adıyla bilinen protokol paketini geliştirmiştir. Bu protokoller, daha sonra INTERNET ağı haline gelen ARPAnet adlı bir ağ üzerinde test edilmiştir. TCP/IP protokolleri, kullanılan ağların ve donanımların yapısından bağımsız olarak iletim ve alım formatlarını ve kurallarını tanımlar.
DARPA tarafından tasarlanan ve TCP/IP protokolleri tarafından yönetilen ağ, bir paket anahtarlamalı ağdır. Bu tür bir ağ, bilgileri ağ üzerinde "paket" adı verilen küçük parçalar halinde iletir. Dolayısıyla, bir bilgisayar büyük bir dosya gönderirse, bu dosya küçük parçalara bölünür ve ağ üzerinden gönderilir; hedef noktaya ulaştığında ise yeniden birleştirilir. TCP/IP, bu paketlerin formatını şu şekilde tanımlar:
- paketin kaynağı
- hedef
- uzunluk
- tür
8.1.2. OSI modeli
TCP/IP protokolleri, ISO (Uluslararası Standartlar Örgütü) tarafından tanımlanan OSI (Açık Sistemler Arabağlantı Referans Modeli) adlı açık ağ modelini büyük ölçüde takip eder. Bu model, makineler arasındaki iletişimin yedi katmanlı bir modelle temsil edilebildiği ideal bir ağı tanımlamaktadır:

Her katman, alt katmandan hizmet alır ve üst katmana kendi hizmetlerini sunar. A ve B adlı farklı makinelerde bulunan iki uygulamanın iletişim kurmak istediğini varsayalım: bu iletişim, Application katmanında gerçekleşir. Ağın işleyişinin tüm ayrıntılarını bilmelerine gerek yoktur: her uygulama, iletmek istediği bilgiyi bir alt katmana, yani Présentation katmanına aktarır. Dolayısıyla uygulamanın, yalnızca Présentation katmanıyla arayüz oluşturma kurallarını bilmesi yeterlidir.
Bilgi, Présentation katmanına ulaştığında, diğer kurallara göre Session katmanına aktarılır ve bu süreç, bilgi fiziksel ortama ulaşana ve hedef makineye fiziksel olarak iletilene kadar devam eder. Orada, gönderen makinede uygulanan işlemin tam tersi işlemden geçecektir.
Her katmanda, bilgiyi göndermekle görevli gönderen işlem, bilgiyi kendisiyle aynı katmana ait diğer makinedeki bir alıcı işleme gönderir. Bunu, katman protokolü olarak adlandırılan belirli kurallara göre yapar. Dolayısıyla, nihai iletişim şeması şu şekildedir:

Farklı katmanların rolleri şöyledir:
Fiziksel bir ortam üzerinden bitlerin iletilmesini sağlar. Bu katmanda, terminal veya bilgisayar gibi veri işleme uç ekipmanları (E.T.T.D.) ile modülatör/demodülatör, çoklayıcı, yoğunlaştırıcı gibi veri devre sonlandırma ekipmanları (E.T.C.D.) bulunur. Bu seviyede dikkat edilmesi gereken noktalar şunlardır: . bilginin kodlama şeklinin seçimi (analog veya dijital) . iletim modunun seçimi (senkron veya asenkron). | |
Fiziksel katmanın fiziksel özelliklerini gizler. İletim hatalarını algılar ve düzeltir. | |
Ağa gönderilen bilgilerin izlemesi gereken yolu yönetir. Buna routage denir: bir bilginin alıcısına ulaşması için izlemesi gereken yolu belirlemek. | |
Önceki katmanlar yalnızca makineler arası iletişime izin verirken, bu katman iki uygulama arasındaki iletişimi mümkün kılar. Bu katman tarafından sağlanan bir hizmet, çoklama olabilir: taşıma katmanı, birden fazla uygulamaya ait bilgileri iletmek için aynı ağ bağlantısını (makineden makineye) kullanabilir. | |
Bu katmanda, bir uygulamanın uzak bir makinede bir çalışma oturumu açmasına ve sürdürmesine olanak tanıyan hizmetler bulunur. | |
Bu katman, farklı makinelerdeki verilerin temsilini standartlaştırmayı amaçlar. Böylece, A makinesinden gelen veriler, ağa gönderilmeden önce A makinesinin Présentation katmanı tarafından standart bir biçime göre "düzenlenir". Veriler, alıcı makine B’nin Présentation katmanına ulaştığında, bu katman verileri standart formatları sayesinde tanıyacak ve makine B’deki uygulamanın tanıyabilmesi için veriler farklı bir şekilde biçimlendirilecektir. | |
Bu aşamada, genellikle e-posta veya dosya aktarımı gibi kullanıcıya yakın uygulamalar bulunur. |
8.1.3. TCP/IP modeli
OSI modeli, henüz hayata geçirilmemiş ideal bir modeldir. TCP/IP protokol dizisi, aşağıdaki şekilde bu modele yaklaşmaktadır:

Fiziksel Katman
Yerel ağlarda genellikle Ethernet veya Token-Ring teknolojisi kullanılır. Burada sadece Ethernet teknolojisini ele alacağız.
Ethernet
Bu isim, 1970'lerin başında PARC Xerox tarafından icat edilen ve 1978'de Xerox, Intel ve Digital Equipment tarafından standartlaştırılan paket anahtarlamalı yerel ağ teknolojisine verilen addır. Ağ fiziksel olarak, çapı yaklaşık 1,27 cm ve uzunluğu en fazla 500 m olan bir koaksiyel kablodan oluşur. répéteurs aracılığıyla genişletilebilir; iki makine arasında en fazla iki tekrarlayıcı bulunabilir. Kablo pasiftir: tüm aktif bileşenler kabloya bağlı cihazlarda bulunur. Her cihaz, aşağıdakileri içeren bir ağ erişim kartı aracılığıyla kabloya bağlanır:
- Kablodaki sinyallerin varlığını algılayan ve analog sinyalleri dijital sinyallere, dijital sinyalleri de analog sinyallere dönüştüren bir verici (transceiver).
- vericiden gelen dijital sinyalleri alan ve bunları işlenmek üzere bilgisayara ileten veya tersi yönde çalışan bir kuplör.
Ethernet teknolojisinin başlıca özellikleri şunlardır:
- 10 Megabit/saniye kapasite.
- Bus topolojisi: tüm cihazlar aynı kabloya bağlanır

- Yayılım ağı - Verici bir makine, alıcı makinenin adresini içeren bilgileri kablo üzerinden iletir. Bağlı tüm makineler bu bilgileri alır, ancak yalnızca bilgilerin adreslendiği makine bu bilgileri saklar.
- Erişim yöntemi şöyledir: İletim yapmak isteyen verici kabloyu dinler; böylece bir taşıyıcı dalganın varlığını veya yokluğunu algılar; bu dalganın varlığı, bir iletimin devam ettiği anlamına gelir. Bu, CSMA (Carrier Sense Multiple Access) tekniğidir. Taşıyıcı dalga yoksa, bir verici sırayla iletim yapmaya karar verebilir. Bu kararı veren birden fazla verici olabilir. Yayınlanan sinyaller birbirine karışır: buna çarpışma denir. Verici bu durumu algılar: kablo üzerinden sinyal gönderirken, aynı anda kabloda gerçekte neyin geçtiğini de dinler. Kabloda geçen bilginin kendi gönderdiği bilgi olmadığını algılarsa, bir çarpışma olduğu sonucuna varır ve sinyal göndermeyi durdurur. Sinyal gönderen diğer vericiler de aynısını yapar. Her verici, kendisine özgü rastgele bir süre geçtikten sonra vermeye yeniden başlayacaktır. Bu tekniğe CD (Çarpışma Algılama) adı verilir. Erişim yöntemi ise CSMA/CD olarak adlandırılır.
- 48 bitlik adresleme. Her makinenin, burada fiziksel adres olarak adlandırılan ve onu kabloya bağlayan kart üzerinde yazılı bir adresi vardır. Bu adrese, makinenin Ethernet adresi denir.
Ağ Katmanı
Bu katmanda IP, ICMP, ARP ve RARP protokollerini buluruz.
IP (İnternet Protokolü) | Ağın iki düğümü arasında paketleri iletir |
ICMP (İnternet Kontrol Mesajı Protokolü) | ICMP, bir makinedeki IP protokol programı ile başka bir makinedeki program arasında iletişimi sağlar. Dolayısıyla, bu protokol, IP protokolünün kendi içinde mesaj alışverişi için kullanılan bir protokoldür. |
ARP (Adres Çözümleme Protokolü) | makinenin İnternet adresi ile makinenin fiziksel adresi arasında eşleştirme yapar |
RARP (Ters Adres Çözümleme Protokolü) | makinenin fiziksel adresi ile makinenin İnternet adresi arasında eşleştirme yapar |
Taşıma/Oturum Katmanları
Bu katmanda aşağıdaki protokoller bulunur:
TCP (İletim Kontrol Protokolü) | İki istemci arasında bilgilerin güvenilir bir şekilde iletilmesini sağlar |
UDP (Kullanıcı Datagram Protokolü) | İki istemci arasında bilgilerin güvenilir olmayan bir şekilde iletilmesini sağlar |
Uygulama/Sunum/Oturum Katmanları
Burada çeşitli protokoller bulunur:
A makinesinin B makinesine terminal olarak bağlanmasını sağlayan terminal emülatörü | |
dosya aktarımına olanak tanır | |
dosya aktarımına olanak tanır | |
ağ kullanıcıları arasında mesaj alışverişine izin verir | |
bir makine adını o makinenin İnternet adresine dönüştürür | |
sun tarafından oluşturulan MicroSystems, makinelerden bağımsız standart bir veri temsilini belirtir | |
Yine Sun tarafından tanımlanmıştır; bu, taşıma katmanından bağımsız, uzak uygulamalar arasındaki bir iletişim protokolüdür. Bu protokol önemlidir: programcının taşıma katmanının ayrıntılarını bilmesi gerekliliğini ortadan kaldırır ve uygulamaları taşınabilir hale getirir. Bu protokol, XDR protokolüne dayanır | |
Yine Sun tarafından tanımlanan bu protokol, bir makinenin başka bir makinenin dosya sistemini "görmesini" sağlar. Bu protokol, yukarıdaki RPC protokolüne dayanır |
8.1.4. İnternet protokollerinin işleyişi
TCP/IP ortamında geliştirilen uygulamalar genellikle bu ortamdaki protokollerin birçoğunu kullanır. Bir uygulama programı, protokollerin en üst katmanıyla iletişim kurar. Bu katman, bilgiyi bir alt katmana aktarır ve bu şekilde fiziksel ortama ulaşana kadar devam eder. Burada bilgi, fiziksel olarak alıcı makineye aktarılır; burada da aynı katmanları, bu kez ters yönde geçerek, gönderilen bilgilerin alıcı uygulamasına ulaşana kadar ilerler. Aşağıdaki şema, bilginin izlediği yolu göstermektedir:

Bir örnek verelim: Application katmanında tanımlanan ve makineler arasında dosya aktarımına olanak tanıyan FTP uygulaması.
- Uygulama, transport. katmanına iletilecek bir bayt dizisi üretir
- transport katmanı, bu bayt dizisini segments ve TCP olarak böler ve her segmentin başına kendi numarasını ekler. Segmentler, IP protokolü tarafından yönetilen Ağ katmanına aktarılır.
- IP katmanı, alınan TCP segmentini kapsülleyen bir paket oluşturur. Bu paketin başına, kaynak ve hedef makinelerin İnternet adreslerini yerleştirir. Ayrıca, alıcı makinenin fiziksel adresini de belirler. Tüm bunlar, Veri Bağlantısı ve Fiziksel Bağlantı katmanına, yani makineyi fiziksel ağa bağlayan ağ kartına aktarılır.
- Burada, IP paketi de bir fiziksel çerçeve içine kapsüllenir ve kablo üzerinden alıcısına gönderilir.
- Hedef makinede, Veri Bağlantısı ve Fiziksel Bağlantı katmanı tersi işlemi gerçekleştirir: IP paketini fiziksel çerçeveden çıkarır ve IP katmanına iletir.
- IP katmanı, paketin doğru olup olmadığını kontrol eder: alınan bitlere göre bir kontrol toplamı hesaplar (checksum) ve bu toplamın paket başlığında bulunması gerektiğini doğrular. Aksi takdirde paket reddedilir.
- Paket doğru olarak onaylanırsa, IP katmanı, paketin içindeki TCP segmentini kapsülden çıkarır ve bir üst katman olan transport katmanına iletir.
- transport katmanı — örneğimizde TCP katmanı — segmentlerin doğru sırasını sağlamak için segment numarasını inceler.
- Ayrıca TCP segmenti için bir doğrulama toplamı hesaplar. Doğruluğu teyit edilirse, TCP katmanı kaynak makineye bir onay mesajı gönderir; aksi takdirde TCP segmenti reddedilir.
- TCP katmanına geriye kalan tek şey, segmentin veri kısmını bir üst katmandaki alıcı uygulamaya iletmektir.
8.1.5. İnternetteki adresleme sorunları
Bir ağdaki noeud, bir bilgisayar, akıllı yazıcı, dosya sunucusu veya aslında TCP/IP protokollerini kullanarak iletişim kurabilen herhangi bir şey olabilir. Her düğümün, ağ türüne bağlı bir biçime sahip bir fiziksel adresi vardır. Bir Ethernet ağında fiziksel adres 6 baytlık bir kodla ifade edilir. Bir X25 ağındaki adres ise 14 basamaklı bir sayıdır.
Bir düğümün İnternet adresi mantıksal bir adrestir: kullanılan donanımdan ve ağdan bağımsızdır. Bu, hem bir yerel ağı hem de bu ağdaki bir düğümü tanımlayan 4 baytlık bir adrestir. İnternet adresi genellikle, 4 baytın değerlerini temsil eden ve nokta ile ayrılmış 4 sayı şeklinde gösterilir. Örneğin, Angers Fen Fakültesi'ndeki Lagaffe adlı bilgisayarın adresi 193.49.144.1, Liny adlı bilgisayarın adresi ise 193.49.144.9 olarak yazılır. Buradan, yerel ağın İnternet adresinin 193.49.144.0 olduğu anlaşılır. Bu ağda en fazla 254 düğüm bulunabilir.
İnternet adresleri veya IP adresleri ağdan bağımsız olduğundan, A ağındaki bir makine, hangi ağda bulunduğuna bakılmaksızın B ağındaki bir makineyle iletişim kurabilir: bunun için sadece IP adresini bilmesi yeterlidir. Her ağın IP protokolü, IP adresi ile fiziksel adres arasında iki yönlü dönüştürme işlemini gerçekleştirir.
IP adreslerinin hepsi birbirinden farklı olmalıdır. Bu adreslerin dağıtımından resmi kurumlar sorumludur. Aslında bu kurumlar, yerel ağlar için bir adres tahsis eder; örneğin, Angers Fen Fakültesi ağı için 193.49.144.0 adresi verilir. Bu ağın yöneticisi daha sonra IP 193.49.144.1 ile 193.49.144.254 arasındaki adresleri istediği gibi atayabilir. Bu adres genellikle ağa bağlı her makinenin özel bir dosyasına kaydedilir.
8.1.5.1. IP adres sınıfları
Bir IP adresi, genellikle I1.I2.I3.I4 olarak gösterilen 4 baytlık bir dizidir ve aslında iki adres içerir:
- ağ adresi
- bu ağdaki bir düğümün adresi
Bu iki alanın boyutuna göre, IP adresleri 3 sınıfa ayrılır: A, B ve C sınıfları.
A sınıfı
IP: I1.I2.I3.I4 adresi, R1.N1.N2.N3 biçimindedir; burada
R1 ağ adresidir
N1.N2.N3 bu ağdaki bir makinenin adresidir
Daha kesin olarak, A sınıfı bir adres olan IP'in biçimi şöyledir:

Ağ adresi 7 bit, düğüm adresi ise 24 bit uzunluğundadır. Dolayısıyla, her biri en fazla 224 düğüm içeren 127 adet A sınıfı ağ olabilir.
B Sınıfı
Burada, IP: I1.I2.I3.I4 adresi, R1.R2.N1.N2 biçimindedir; burada
R1.R2 ağ adresidir
N1.N2 bu ağdaki bir makinenin adresidir
Daha kesin olarak, B sınıfı bir adres olan IP'in biçimi şöyledir:

Ağ adresi ve düğüm adresi 2 bayt (tam olarak 14 bit) uzunluğundadır. Dolayısıyla, her biri en fazla 2¹⁶ düğüm içeren 2¹⁴ adet B sınıfı ağ olabilir.
C Sınıfı
Bu sınıfta, IP: I1.I2.I3.I4 adresi, R1.R2.R3.N1 biçimindedir; burada
R1.R2.R3 ağ adresidir
N1 bu ağdaki bir makinenin adresidir
Daha kesin olarak, C sınıfı bir adres olan IP'in biçimi şöyledir:

Ağ adresi 3 bayt (3 bit hariç) ve düğüm adresi 1 bayttır. Dolayısıyla, en fazla 256 düğüm içeren 221 adet C sınıfı ağ olabilir.
Angers Fen Fakültesi'ne ait Lagaffe makinesinin adresi 193.49.144.1 olduğundan, en üst baytın değeri 193, yani ikili sistemde 11000001 olduğu görülür. Bundan, ağın C sınıfı olduğu sonucuna varılır.
Ayrılmış adresler
. Bazı IP adresleri, ağdaki düğüm adresleri değil, ağ adresleridir. Bunlar, düğüm adresinin 0 olarak ayarlandığı adreslerdir. Dolayısıyla, 193.49.144.0 adresi, Angers Fen Fakültesi ağının IP adresidir. Sonuç olarak, bir ağdaki hiçbir düğümün adresi sıfır olamaz.
. IP adresinde düğüm adresi yalnızca 1'lerden oluşuyorsa, bu bir yayın adresidir: bu adres ağdaki tüm düğümleri belirtir.
. Teorik olarak 28=256 düğüme izin veren bir C sınıfı ağda, yasaklanmış iki adresi çıkarırsak, geriye sadece 254 izin verilen adres kalır.
8.1.5.2. İnternet Adresi <--> Fiziksel Adres dönüştürme protokolleri
Bir makineden diğerine bilgi aktarımı sırasında, bu bilgilerin IP katmanından geçerken paketler halinde kapsüllendiğini görmüştük. Bu paketler şu şekildedir:

Dolayısıyla, IP paketi, kaynak ve hedef makinelerin İnternet adreslerini içerir. Bu paket, fiziksel ağa gönderilmek üzere sorumlu katmana iletildiğinde, nihayetinde ağa gönderilecek fiziksel çerçeveyi oluşturmak üzere başka bilgiler de eklenir. Örneğin, bir Ethernet ağındaki çerçevenin biçimi şöyledir:

Nihai çerçevede, kaynak ve hedef makinelerin fiziksel adresleri bulunur. Bunlar nasıl elde edilir?
Gönderen makine, iletişim kurmak istediği makinenin IP adresini bildiğinden, ARP (Adres Çözümleme Protokolü) adı verilen özel bir protokolü kullanarak bu makinenin fiziksel adresini elde eder.
- Fiziksel adresini bulmak istediği makinenin IP adresini içeren, ARP adı verilen özel bir paket gönderir. Ayrıca, bu pakete kendi IP adresini ve fiziksel adresini de eklemeyi ihmal etmemiştir.
- Bu paket, ağdaki tüm düğümlere gönderilir.
- Düğümler, paketin özel niteliğini fark eder. Pakette kendi adresi olan IP'i tanıyan düğüm, paketin gönderenine fiziksel adresini göndererek yanıt verir. Bunu nasıl yapabilir? Pakette, gönderenin IP adresini ve fiziksel adresini bulmuştur.
- Böylece gönderen, aradığı fiziksel adresi alır. Aynı alıcıya başka paketler gönderilmesi gerektiğinde daha sonra kullanabilmek için bu adresi bellekte saklar.
Bir makinenin IP adresi normalde, makinenin bu adresi öğrenmek için başvurabileceği dosyalarından birinde kayıtlıdır. Bu adres değiştirilebilir: dosyayı düzenlemek yeterlidir. Fiziksel adres ise ağ kartının belleğinde kayıtlıdır ve değiştirilemez.
Bir yönetici ağını farklı bir şekilde düzenlemek istediğinde, tüm düğümlerin IP adreslerini değiştirmek ve dolayısıyla farklı düğümlerin farklı yapılandırma dosyalarını düzenlemek zorunda kalabilir. Bu işlem, çok sayıda makine varsa zahmetli olabilir ve hatalara yol açabilir. Bir yöntem, makinelere IP adresi atamamaktır: bu durumda, makinenin IP adresini bulması gereken dosyaya özel bir kod yazılır. IP adresine sahip olmadığını fark eden makine, RARP (Ters Adres Çözümleme Protokolü) adı verilen bir protokol aracılığıyla bu adresi talep eder. Ardından, önceki ARP paketine benzer şekilde, fiziksel adresini içeren RARP adlı özel bir paketi ağa gönderir. Bu paket, RARP paketini tanıyan tüm düğümlere gönderilir. Bunlardan biri, RARP sunucusu olarak adlandırılan sunucu, tüm düğümlerin fiziksel adres <--> IP adresi eşleşmelerini içeren bir dosyaya sahiptir. Ardından, RARP paketinin gönderenine yanıt vererek, ona IP adresini geri gönderir. Ağını yeniden yapılandırmak isteyen bir yönetici, bu nedenle RARP sunucusunun eşleme dosyasını düzenlemesi yeterlidir. Bu sunucunun normalde sabit bir IP adresi olmalıdır ve kendisi RARP protokolünü kullanmaya gerek kalmadan bu adresi bilmelidir.
8.1.6. İnternetin IP katmanı olarak adlandırılan ağ katmanı
IP protokolü (İnternet Protokolü), paketlerin alması gereken biçimi ve gönderilme veya alınma sırasında nasıl yönetilmeleri gerektiğini tanımlar. Bu özel paket türüne IP datagramı denir. Bunu daha önce tanıtmıştık:

Önemli olan nokta, IP datagramının iletilecek verilerin yanı sıra, kaynak ve hedef makinelerin İnternet adreslerini de içermesidir. Böylece alıcı makine, mesajı kimin gönderdiğini bilir.
Geçiş yaptığı ağın fiziksel özelliklerine göre uzunluğu belirlenen bir ağ çerçevesinden farklı olarak, IP datagramının uzunluğu yazılım tarafından belirlenir ve bu nedenle farklı fiziksel ağlarda aynı olacaktır. Ağ katmanından fiziksel katmana doğru inerken, IP datagramının bir fiziksel çerçeve içine kapsüle edildiğini görmüştük. Ethernet ağındaki fiziksel çerçeve örneğini vermiştik:

Fiziksel çerçeveler, gönderen makinenin bulunduğu fiziksel ağda olmayabilecek hedeflerine doğru düğümden düğüme ilerler. Dolayısıyla, IP paketi, farklı türdeki iki ağı birbirine bağlayan düğümlerde art arda farklı fiziksel çerçevelere kapsüllenebilir. Ayrıca, IP paketinin bir fiziksel çerçeveye kapsüllenemeyecek kadar büyük olması da mümkündür. Bu sorunun yaşandığı düğümdeki IP yazılımı, IP paketini belirli kurallara göre fragments'e böler ve her biri daha sonra fiziksel ağ üzerinden gönderilir. Bu paketler ancak nihai varış noktalarında yeniden birleştirilir.
8.1.6.1. Yönlendirme
Yönlendirme, IP paketlerinin hedeflerine ulaştırılması yöntemidir. İki yöntem vardır: doğrudan yönlendirme ve dolaylı yönlendirme.
Doğrudan yönlendirme
Doğrudan yönlendirme, bir IP paketinin aynı ağ içinde göndereninden alıcısına doğrudan iletilmesini ifade eder:
- Bir IP datagramını gönderen makine, alıcının IP adresine sahiptir.
- Bu makine, alıcının fiziksel adresini ARP protokolü aracılığıyla veya bu adres daha önce elde edilmişse kendi tablolarından alır.
- Paketi, ağ üzerinden bu fiziksel adrese gönderir.
Dolaylı yönlendirme
Dolaylı yönlendirme, IP paketinin, gönderenin ait olduğu ağdan farklı bir ağda bulunan bir hedefe iletilmesini ifade eder. Bu durumda, kaynak ve hedef makinelerin IP adreslerinin ağ adresi kısımları birbirinden farklıdır. Kaynak makine bu durumu fark eder. Ardından paketi, yönlendirici (router) adı verilen özel bir düğüme gönderir; bu düğüm, bir yerel ağı diğer ağlara bağlar ve kaynak makine, bu düğümün IP adresini kendi tablolarında bulur. Bu adres, başlangıçta bir dosyadan, kalıcı bir bellekten veya ağ üzerinde dolaşan bilgiler yoluyla elde edilmiştir.
Bir yönlendirici iki ağa bağlıdır ve bu iki ağın içinde IP adresine sahiptir.

Yukarıdaki örneğimizde:
- 1 numaralı ağın İnternet adresi 193.49.144.0, 2 numaralı ağın adresi ise 193.49.145.0'dır.
- 1 numaralı ağın içinde yönlendiricinin adresi 193.49.144.6, 2 numaralı ağın içinde ise 193.49.145.3'tür.
Yönlendiricinin görevi, aldığı ve 1 numaralı ağa özgü bir fiziksel çerçeve içinde bulunan IP paketini, 2 numaralı ağda iletilebilecek bir fiziksel çerçeveye dönüştürmektir. Paketin alıcısının IP adresi 2 numaralı ağda bulunuyorsa, yönlendirici paketi doğrudan alıcıya gönderir; aksi takdirde paketi, 2 numaralı ağı 3 numaralı ağa bağlayan başka bir yönlendiriciye gönderir ve bu süreç böyle devam eder.
8.1.6.2. Hata ve kontrol mesajları
Yine ağ katmanında, yani IP protokolüyle aynı seviyede, ICMP (İnternet Kontrol Mesajı Protokolü) protokolü bulunur. Bu protokol, ağın iç işleyişiyle ilgili mesajlar (arıza veren düğümler, bir yönlendiricide tıkanma vb.) göndermek için kullanılır. ICMP mesajları, IP paketleri içine kapsüllenir ve ağ üzerinden gönderilir. Farklı düğümlerin IP katmanları, aldıkları ICMP mesajlarına göre uygun eylemleri gerçekleştirir. Böylece, bir uygulama, ağa özgü bu sorunları hiçbir zaman görmez.
Bir düğüm, ICMP bilgilerini kullanarak yönlendirme tablolarını günceller.
8.1.7. Taşıma katmanı: UDP ve TCP protokolleri
8.1.7.1. UDP protokolü: Kullanıcı Datagram Protokolü
UDP protokolü, iki nokta arasında güvenilir olmayan veri alışverişine olanak tanır; yani bir paketin hedefine doğru bir şekilde ulaştırılması garanti edilmez. Uygulama, isterse bunu kendisi yönetebilir; örneğin, bir mesajı gönderdikten sonra bir onay mesajı bekleyerek bir sonraki mesajı gönderebilir.
Şu an için ağ düzeyinde, makinelerin IP adreslerinden bahsettik. Oysa bir makinede, birbirleriyle iletişim kurabilen farklı işlemler aynı anda bir arada bulunabilir. Bu nedenle, bir mesaj gönderilirken sadece alıcı makinenin IP adresini değil, aynı zamanda alıcı işlemin "adını" da belirtmek gerekir. Bu ad aslında bir sayıdır ve buna bağlantı noktası numarası denir. Bazı numaralar standart uygulamalar için ayrılmıştır: örneğin, tftp (trivial file transfer protocol) uygulaması için 69 numaralı bağlantı noktası.
UDP protokolü tarafından yönetilen paketlere datagram da denir. Bu paketler şu biçimdedir:

Bu datagramlar, IP paketleri içine, ardından da fiziksel çerçeveler içine kapsüllenir.
8.1.7.2. TCP Protokolü: Aktarım Kontrol Protokolü
Güvenli iletişim için UDP protokolü yetersizdir: uygulama geliştiricisi, paketlerin doğru şekilde iletildiğini tespit etmesini sağlayan bir protokolü kendisi geliştirmelidir.
TCP protokolü (Aktarım Kontrol Protokolü) bu sorunları ortadan kaldırır. Özellikleri şunlardır:
- Gönderim yapmak isteyen işlem, öncelikle göndereceği bilgilerin alıcı işlemiyle bir bağlantı kurar. Bu bağlantı, gönderen makinenin bir bağlantı noktası ile alıcı makinenin bir bağlantı noktası arasında kurulur. İki bağlantı noktası arasında böylece sanal bir yol oluşturulur ve bu yol, bağlantıyı kuran sadece bu iki işlem için ayrılır.
- Kaynak işlem tarafından gönderilen tüm paketler bu sanal yolu izler ve gönderildikleri sırayla ulaşır; bu durum, paketlerin farklı yollar izleyebilmesi nedeniyle UDP protokolünde garanti edilemiyordu.
- Gönderilen bilgi sürekli bir yapıya sahiptir. Gönderen işlem, kendi ritminde bilgi gönderir. Bu bilgiler mutlaka hemen gönderilmez: TCP protokolü, gönderilmek üzere yeterli miktarda bilgi birikene kadar bekler. Bilgiler, TCP segmenti adı verilen bir yapıda depolanır. Bu segment dolduğunda, IP katmanına iletilir ve burada bir IP paketine kapsüllenir.
- TCP protokolüyle gönderilen her segment numaralandırılır. Alıcı TCP protokolü, segmentleri sırayla aldığını doğrular. Düzgün bir şekilde alınan her segment için, gönderene bir alım onayı gönderir.
- Gönderici bu onayı aldığında, bunu gönderen işleme bildirir. Böylece gönderen, bir segmentin başarıyla ulaştığını öğrenebilir; bu, UDP protokolüyle mümkün değildi.
- Bir süre geçtikten sonra, bir segmenti gönderen TCP protokolü bir onay mesajı almazsa, söz konusu segmenti yeniden gönderir ve böylece bilgi aktarım hizmetinin kalitesini garanti eder.
- İletişim kuran iki işlem arasında kurulan sanal devre full-duplex'tir: bu, bilginin her iki yönde de aktarılabileceği anlamına gelir. Böylece, kaynak işlem bilgi göndermeye devam ederken hedef işlem onay mesajları gönderebilir. Bu, örneğin kaynak TCP protokolünün, onay mesajını beklemeden birden fazla segment göndermesine olanak tanır. Belirli bir süre sonra n numaralı bir segmentin onay mesajını almadığını fark ederse, segmentlerin gönderimine bu noktadan itibaren devam eder.
8.1.8. Uygulamalar Katmanı
UDP ve TCP protokollerinin üzerinde çeşitli standart protokoller bulunmaktadır:
TELNET
Bu protokol, ağdaki A makinesindeki bir kullanıcının B makinesine (genellikle ana makine olarak adlandırılır) bağlanmasına olanak tanır. TELNET, A makinesinde evrensel olarak adlandırılan bir terminali taklit eder. Böylece kullanıcı, B makinesine bağlı bir terminale sahipmiş gibi davranır. Telnet, TCP protokolünü kullanır.
FTP: (Dosya Aktarım Protokolü)
Bu protokol, iki uzak makine arasında dosya alışverişine ve örneğin dizin oluşturma gibi dosya işlemlerine olanak tanır. TCP protokolünü kullanır.
TFTP: (Basit Dosya Aktarım Kontrolü)
Bu protokol, FTP protokolünün bir varyantıdır. UDP protokolüne dayanır ve FTP protokolünden daha az gelişmiştir.
DNS: (Etki Alanı Adı Sistemi)
Bir kullanıcı, örneğin FTP aracılığıyla uzak bir makineyle dosya alışverişi yapmak istediğinde, bu makinenin İnternet adresini bilmesi gerekir. Örneğin, Angers Üniversitesi'ndeki Lagaffe adlı makinede FTP yapmak için, FTP'i şu şekilde çalıştırmak gerekir: FTP 193.49.144.1
Bu durum, makine <--> IP adresi eşleşmesini içeren bir dizin bulunmasını gerektirir. Muhtemelen bu dizinde makineler şu tür sembolik adlarla belirtilecektir:
Angers Üniversitesi'ne ait DPX2/320 makinesi
Angers'daki ISERPA Sun makinesi
Bir makineyi IP adresiyle değil de bir adla tanımlamanın daha kolay olacağı açıktır. Bu durumda adların benzersizliği sorunu ortaya çıkar: birbirine bağlı milyonlarca makine vardır. Adların bir merkezi kurum tarafından tahsis edildiğini düşünebiliriz. Bu şüphesiz oldukça zahmetli olurdu. Aslında adların yönetimi alanlara dağıtılmıştır. Her alan adı, genellikle çok küçük çaplı bir kurum tarafından yönetilir ve bu kurum, makine isimlerinin seçiminde tam bir serbestliğe sahiptir. Dolayısıyla Fransa’daki makineler, Paris’teki Inria tarafından yönetilen fr alan adına aittir. İşleri daha da basitleştirmek için kontrol yetkisi bir kez daha dağıtılır: fr alan adı içinde alt alan adları oluşturulur. Böylece Angers Üniversitesi, univ-Angers alan adına aittir. Bu etki alanını yöneten birim, Angers Üniversitesi ağındaki makinelere isim verme konusunda tam bir serbestliğe sahiptir. Şu an için bu etki alanı alt bölümlere ayrılmamıştır. Ancak, ağda çok sayıda makine barındıran büyük bir üniversitede, bu etki alanı alt bölümlere ayrılabilir.
Angers Üniversitesi’ne ait DPX2/320, Lagaffe olarak adlandırılırken, PC ve 486DX50 ise liny olarak adlandırılmıştır. Bu makinelere dışarıdan nasıl referans verilir? Ait oldukları etki alanı hiyerarşisini belirterek. Böylece Lagaffe adlı makinenin tam adı şöyle olacaktır:
Etki alanlarının içinde göreceli adlar kullanılabilir. Dolayısıyla, fr etki alanı içinde ve univ-Angers etki alanı dışında, Lagaffe makinesine şu şekilde atıfta bulunulabilir:
Son olarak, univ-Angers etki alanı içinde, bu makineye basitçe şu şekilde atıfta bulunulabilir:
Dolayısıyla bir uygulama, bir makineye adıyla atıfta bulunabilir. Sonuçta yine de bu makinenin İnternet adresini elde etmek gerekir. Bu nasıl gerçekleştirilir? A makinesinden B makinesiyle iletişim kurmak istediğimizi varsayalım.
- B makinesi, A makinesiyle aynı etki alanına aitse, muhtemelen A makinesindeki bir dosyada B makinesinin IP adresi bulunacaktır.
- Aksi takdirde, A makinesi başka bir dosyada ya da daha önce bahsedilen dosyada, adresleri IP olan birkaç ad sunucusunun listesini bulacaktır. Bir ad sunucusu, bir makine adı ile adresi IP arasındaki eşleşmeyi sağlamakla görevlidir. A makinesi, listesindeki ilk ad sunucusuna, aranan makinenin adını içeren ve DNS olarak adlandırılan özel bir sorgu gönderecektir. Sorgulanan sunucunun kayıtlarında bu ad varsa, A makinesine karşılık gelen IP adresini gönderecektir. Aksi takdirde, sunucu da kendi dosyalarında sorgulayabileceği bir ad sunucuları listesi bulacaktır. Ardından bu sunucuları sorgulayacaktır. Böylece, bir dizi ad sunucusu sorgulanacaktır; bu işlem rastgele değil, istek sayısını en aza indirecek şekilde gerçekleştirilecektir. Makine nihayetinde bulunursa, yanıt Makine A’ya geri gönderilecektir.
XDR: (eXternal Veri Temsili)
Sun tarafından oluşturulan MicroSystems protokolü, makinelerden bağımsız standart bir veri temsilini tanımlar.
RPC: (Uzak Yordam Çağrısı)
Yine sun tarafından tanımlanan bu protokol, aktarım katmanından bağımsız olarak uzak uygulamalar arasında iletişim sağlayan bir protokoldür. Bu protokol önemlidir: programcının aktarım katmanının ayrıntılarını bilmesini gerektirmez ve uygulamaları taşınabilir hale getirir. Bu protokol, XDR protokolüne dayanır
NFS: Ağ Dosya Sistemi
Yine Sun tarafından tanımlanan bu protokol, bir makinenin başka bir makinenin dosya sistemini "görmesini" sağlar. Bu protokol, yukarıdaki RPC protokolüne dayanmaktadır.
8.1.9. Sonuç
Bu giriş bölümünde İnternet protokollerinin temel hatlarını ele aldık. Bu konuyu daha derinlemesine incelemek isteyenler, Douglas Comer'ın mükemmel kitabını okuyabilirler:
Başlık: TCP/IP: Mimari, Protokoller, Uygulamalar.
Yazar: Douglas COMER
Yayınevi: InterEditions
8.2. Java'da ağ adreslerinin yönetimi
8.2.1. Tanım
İnternetteki her makine, benzersiz bir adres veya adla tanımlanır. Bu iki öğe, Java'da InetAddress sınıfı tarafından yönetilir; bu sınıfın bazı yöntemleri şunlardır:
IP adresinin 4 baytını, InetAddress örneğinin geçerli adresi olarak verir | |
InetAddress örneğinin IP adresini verir | |
geçerli InetAddress örneğinin İnternet adını verir | |
IP adres kimliğini/InetAddress örneğinin internet adını verir | |
Host ile belirtilen makinenin InetAddress örneğini oluşturur. Host bilinmiyorsa bir istisna oluşturur. Host, bir makinenin internet adı veya IP biçimindeki I1.I2.I3.I4 adresi olabilir | |
bu komutu içeren programın çalıştığı makinenin InetAddress örneğini oluşturur. |
8.2.2. Bazı örnekler
8.2.2.1. Yerel makineyi tanımlama
import java.net.*;
public class localhost{
public static void main (String arg[]){
try{
InetAddress adresse=InetAddress.getLocalHost();
byte[] IP=adresse.getAddress();
System.out.print("IP=");
int i;
for(i=0;i<IP.length-1;i++) System.out.print(IP[i]+".");
System.out.println(IP[i]);
System.out.println("adresse="+adresse.getHostAddress());
System.out.println("nom="+adresse.getHostName());
System.out.println("identité="+adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getLocalHost : "+e);
}// try bloğu sonu
}// main sonu
}// class sonu
Çalıştırma sonuçları şöyledir:
Her makinenin 127.0.0.1 olan bir dahili IP adresi vardır. Bir program bu ağ adresini kullandığında, üzerinde çalıştığı makineyi kullanır. Bu adresin avantajı, ağ kartı gerektirmemesidir. Böylece, bir ağa bağlı olmadan ağ programlarını test etmek mümkündür. Yerel bilgisayarı belirtmenin bir başka yolu da localhost adını kullanmaktır.
8.2.2.2. Herhangi bir bilgisayarı tanımlama
import java.net.*;
public class getbyname{
public static void main (String arg[]){
String nomMachine;
// argümanı alınır
if(arg.length==0)
nomMachine="localhost";
else nomMachine=arg[0];
// makinenin adresini almaya çalışıyoruz
try{
InetAddress adresse=InetAddress.getByName(nomMachine);
System.out.println("IP : "+ adresse.getHostAddress());
System.out.println("nom : "+ adresse.getHostName());
System.out.println("identité : "+ adresse);
} catch (UnknownHostException e){
System.out.println ("Erreur getByName : "+e);
}// try sonu
}// main sonu
}// sınıf sonu
java getbyname çağrısıyla şu sonuçlar elde edilir:
java getbyname shiva.istia.univ-angers.fr çağrısı ile şu sonuçlar elde edilir:
java getbyname www.ibm.com çağrısı ile şu sonuç elde edilir:
8.3. İletişim TCP-IP
8.3.1. Genel Bilgiler

A makinesindeki bir AppA uygulaması, İnternet'teki B makinesindeki bir AppB uygulamasıyla iletişim kurmak istediğinde, birkaç bilgiye sahip olması gerekir:
- B makinesinin IP adresi veya adı
- AppB uygulamasının çalıştığı bağlantı noktası numarası. Zira B makinesi, İnternet üzerinde çalışan birçok uygulamayı destekleyebilir. Ağdan gelen bilgileri aldığında, bu bilgilerin hangi uygulamaya yönelik olduğunu bilmesi gerekir. B makinesindeki uygulamalar, iletişim portları olarak da adlandırılan arayüzler aracılığıyla ağa erişir. Bu bilgi, B makinesi tarafından alınan pakette bulunur ve böylece paket doğru uygulamaya iletilebilir.
- B makinesinin anladığı iletişim protokolleri. Çalışmamızda yalnızca TCP-IP protokollerini kullanacağız.
- AppB uygulaması tarafından kabul edilen iletişim protokolü. Nitekim, A ve B makineleri birbirleriyle “iletişim kuracak”. Birbirlerine söyleyecekleri şeyler, TCP-IP protokollerinde kapsüllenecektir. Bununla birlikte, zincirin sonunda AppB uygulaması, AppA uygulaması tarafından gönderilen bilgiyi aldığında, bu bilgiyi yorumlayabilmesi gerekir. Bu durum, A ve B adlı iki kişinin telefonla iletişim kurmasına benzemektedir: diyalogları telefon aracılığıyla aktarılır. Konuşma, A’nın telefonunda sinyal biçiminde kodlanır, telefon hatları üzerinden taşınır ve B’nin telefonuna ulaştığında burada kod çözülür. B kişisi bu şekilde konuşmaları duyar. İşte burada “iletişim protokolü” kavramı devreye girer: Eğer A Fransızca konuşuyorsa ve B bu dili anlamıyorsa, A ve B arasında anlamlı bir iletişim kurulamaz.
Bu nedenle, iletişim kuran iki uygulama, hangi diyalog türünü benimseyecekleri konusunda anlaşmalıdır. Örneğin, ftp hizmetiyle yapılan diyalog, pop hizmetiyle yapılan diyalogla aynı değildir: bu iki hizmet aynı komutları kabul etmez. Her ikisinin de farklı bir iletişim protokolü vardır.
8.3.2. TCP protokolünün özellikleri
Burada yalnızca TCP aktarım protokolünü kullanan ağ iletişimlerini inceleyeceğiz. Bu protokolün özelliklerini hatırlayalım:
- Veriyi göndermek isteyen işlem, öncelikle göndereceği bilgilerin alıcı işlemiyle bir bağlantı kurar. Bu bağlantı, gönderen makinenin bir bağlantı noktası ile alıcı makinenin bir bağlantı noktası arasında kurulur. İki bağlantı noktası arasında böylece sanal bir yol oluşturulur ve bu yol, bağlantıyı kuran sadece bu iki işleme ayrılır.
- Kaynak işlem tarafından gönderilen tüm paketler bu sanal yolu takip eder ve gönderildikleri sırayla ulaşır
- Gönderilen bilgi sürekli bir yapıya sahiptir. Gönderen süreç, kendi hızında bilgi gönderir. Bu bilgiler mutlaka hemen gönderilmez: TCP protokolü, gönderilmek üzere yeterli miktarda bilgi birikene kadar bekler. Bilgiler, TCP segmenti adı verilen bir yapıda depolanır. Bu segment dolduğunda, IP katmanına iletilir ve burada bir IP paketine kapsüllenir.
- TCP protokolü tarafından gönderilen her segment numaralandırılır. Alıcı TCP protokolü, segmentleri sırayla aldığını doğrular. Düzgün bir şekilde alınan her segment için, gönderene bir alım onayı gönderir.
- Gönderici, bu onayı aldığında bunu gönderen işleme bildirir. Böylece gönderen, bir segmentin başarıyla ulaştığını öğrenebilir.
- Belirli bir süre geçtikten sonra, bir segmenti gönderen TCP protokolü bir onay mesajı almazsa, söz konusu segmenti yeniden gönderir ve böylece bilgi aktarım hizmetinin kalitesini garanti eder.
- İletişim kuran iki işlem arasında kurulan sanal devre full-duplex'tir: bu, bilginin her iki yönde de aktarılabileceği anlamına gelir. Böylece, kaynak işlem bilgi göndermeye devam ederken hedef işlem de onay mesajları gönderebilir. Bu, örneğin kaynak TCP protokolünün, onay mesajını beklemeden birden fazla segment göndermesine olanak tanır. Belirli bir süre sonra n numaralı bir segmentin onay mesajını almadığını fark ederse, segmentlerin gönderimine bu noktadan itibaren devam eder.
8.3.3. İstemci-sunucu ilişkisi
İnternet üzerindeki iletişim genellikle asimetriktir: A makinesi, B makinesinden bir hizmet talep etmek üzere bir bağlantı başlatır; B makinesinin SB1 hizmetiyle bağlantı kurmak istediğini belirtir. B makinesi bunu kabul eder veya reddeder. Kabul ederse, A makinesi isteklerini SB1 hizmetine gönderebilir. Bu istekler, SB1 hizmeti tarafından anlaşılan iletişim protokolüne uymak zorundadır. Böylece, istemci makine olarak adlandırılan A makinesi ile sunucu makine olarak adlandırılan B makinesi arasında bir istek-yanıt diyaloğu kurulur. İki tarafın biri bağlantıyı sonlandıracaktır.
8.3.4. Bir istemcinin mimarisi
Bir sunucu uygulamasının hizmetlerini talep eden bir ağ programının mimarisi şu şekilde olacaktır:
ouvrir la connexion avec le service SB1 de la machine B
si réussite alors
tant que ce n'est pas fini
préparer une demande
l'émettre vers la machine B
attendre et récupérer la réponse
la traiter
fin tant que
finsi
8.3.5. Bir sunucunun mimarisi
Hizmet sunan bir programın mimarisi şu şekilde olacaktır:
ouvrir le service sur la machine locale
tant que le service est ouvert
se mettre à l'écoute des demandes de connexion sur un port dit port d'écoute
lorsqu'il y a une demande, la faire traiter par une autre tâche sur un autre port dit port de service
fin tant que
Sunucu programı, bir istemcinin ilk bağlantı talebini, hizmet almak amacıyla yaptığı sonraki taleplerinden farklı şekilde işler. Program, hizmeti kendisi sağlamaz. Eğer bunu yapsaydı, hizmet süresince bağlantı taleplerini dinleyemezdi ve istemcilere hizmet verilemezdi. Bu nedenle farklı bir yol izler: Dinleme bağlantı noktasında bir bağlantı isteği alındığında ve kabul edildiğinde, sunucu, istemcinin talep ettiği hizmeti sunmakla görevli bir görev oluşturur. Bu hizmet, sunucu makinesinin hizmet bağlantı noktası olarak adlandırılan başka bir bağlantı noktası üzerinden sunulur. Böylelikle aynı anda birden fazla istemciye hizmet verilebilir.
Bir hizmet görevi aşağıdaki yapıya sahip olacaktır:
tant que le service n'a pas été rendu totalement
attendre une demande sur le port de service
lorsqu'il y en a une, élaborer la réponse
transmettre la réponse via le port de service
fin tant que
libérer le port de service
8.3.6. Socket sınıfı
8.3.6.1. Définition
İnternet üzerinden iletişim kuran programlar tarafından kullanılan temel araç socket'tir. Bu İngilizce kelime "elektrik prizi" anlamına gelir. Burada anlamı genişletilerek "ağ prizi" olarak kullanılmaktadır. Bir uygulamanın İnternet ağı üzerinden bilgi gönderebilmesi ve alabilmesi için bir ağ prizine, yani bir socket'e ihtiyacı vardır. Bu araç ilk olarak Berkeley Üniversitesi'nin Unix sürümlerinde geliştirilmiştir. Daha sonra tüm Unix sistemlerine ve Windows ortamına da uyarlanmıştır. Ayrıca Java sanal makinelerinde iki farklı biçimde mevcuttur: istemci uygulamaları için Socket sınıfı ve sunucu uygulamaları için ServerSocket sınıfı. Burada, Socket sınıfının bazı oluşturucularını ve yöntemlerini açıklıyoruz:
host makinesinin port bağlantı noktası üzerinden bir uzaktan bağlantı kurar |
soket tarafından kullanılan yerel bağlantı noktası numarasını döndürür | |||
soketin bağlı olduğu uzak bağlantı noktası numarasını döndürür | |||
soketin bağlı olduğu yerel adresi döndürür | |||
soketin bağlı olduğu uzak adresi döndürür | |||
uzaktaki ortağın gönderdiği verileri okumaya olanak tanıyan bir giriş akışı oluşturur | |||
uzaktaki ortağa veri göndermek için bir çıkış akışı döndürür | |||
soketin giriş akışını kapatır | |||
soketin çıkış akışını kapatır | |||
soketi ve G/Ç akışlarını kapatır | |||
soketi "temsil eden" bir karakter dizisi döndürür | |||
8.3.6.2. Bir sunucu makinesiyle bağlantı açma
Gördüğümüz gibi, A makinesinin B makinesindeki bir hizmete bağlantı açabilmesi için iki bilgiye ihtiyacı vardır:
- B makinesinin adresi IP veya adı
- istenen hizmetin çalıştığı bağlantı noktası numarası
Yapıcı
bir soket oluşturur ve bunu host makinesine port bağlantı noktası üzerinden bağlar. Bu yapıcı, çeşitli durumlarda bir istisna oluşturur:
- yanlış adres
- yanlış bağlantı noktası
- istek reddedildi
- …
Bu istisnayı yönetmemiz gerekiyor:
Socket sClient=null;
try{
sClient=new Socket(host,port);
} catch(Exception e){
// bağlantı başarısız oldu - hata işleniyor
….
}
Bağlantı isteği başarılı olursa, istemciye B makinesiyle iletişim kurması için yerel olarak bir bağlantı noktası atanır. Bağlantı kurulduktan sonra, bu bağlantı noktası şu yöntemle öğrenilebilir:
Bağlantı başarılı olursa, sunucunun kendi tarafında, hizmet olarak adlandırılan bir bağlantı noktasında çalışan başka bir görev aracılığıyla hizmeti sağladığını görmüştük. Bu bağlantı noktası numarası şu yöntemle öğrenilebilir:
8.3.6.3. Ağa bilgi gönderme
Aşağıdaki yöntemle soket üzerinden ve dolayısıyla ağ üzerinden bir yazma akışı elde edilebilir:
Bu akışa gönderilen her şey, sunucu makinesinin hizmet bağlantı noktasında alınacaktır. Birçok uygulama, satır sonu ile biten metin satırları şeklinde bir diyaloga sahiptir. Bu nedenle, println yöntemi bu durumlarda oldukça kullanışlıdır. Böylece OutputStream çıkış akışını, println yöntemine sahip olan PrintWriter akışına dönüştürürüz. Yazma işlemi bir istisna oluşturabilir.
8.3.6.4. Ağdan gelen bilgileri okuma
Sokete gelen bilgileri okumak için şu yöntem kullanılabilir:
Bu akışta okunan her şey, sunucu makinesinin hizmet bağlantı noktasından gelir. Satır sonu ile biten metin satırları şeklinde diyalog içeren uygulamalar için readLine yöntemini kullanmak tercih edilir. Bunun için InputStream giriş akışını, readLine() yöntemine sahip olan BufferedReader akışına dönüştürürüz. Okuma işlemi bir istisna oluşturabilir.
8.3.6.5. Bağlantının kapatılması
Bu işlem şu yöntemle gerçekleştirilir:
Bu yöntem bir istisna oluşturabilir. Kullanılan kaynaklar, özellikle ağ bağlantı noktası, serbest bırakılır.
8.3.6.6. İstemci mimarisi
Artık bir internet istemcisinin temel mimarisini tanımlamak için gerekli unsurlara sahibiz:
Socket sClient=null;
try{
// M makinesinin P bağlantı noktasında çalışan servise bağlanılıyor
sClient=new Socket(M,P);
// istemci soketinin giriş-çıkış akışları oluşturuluyor
BufferedReader in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
PrintWriter out=new PrintWriter(sClient.getOutputStream(),true);
// istek-yanıt döngüsü
boolean fini=false;
String demande;
String réponse;
while (! fini){
// istek hazırlanıyor
demande=…
// istek gönderilir
out.println(demande);
// yanıt okunur
réponse=in.readLine();
// yanıt işleniyor
…
}
// işlem tamamlandı
sClient.close();
} catch(Exception e){
// istisna işleniyor
….
}
Örneği karmaşıklaştırmamak için, Socket oluşturucusu veya readline, getInputStream, getOutputStream ve close yöntemleri tarafından üretilen farklı istisna türlerini yönetmeye çalışmadık. Her şey tek bir istisna altında birleştirildi.
8.3.7. ServerSocket sınıfı
8.3.7.1. Définition
Bu sınıf, sunucu tarafında soketlerin yönetimi için tasarlanmıştır. Burada, bu sınıfın bazı oluşturucularını ve yöntemlerini açıklayacağız:
port bağlantı noktasında dinleme soketi oluşturur | |
aynı işlevi yerine getirir, ancak count ile kuyruk boyutunu, c.a.d ile ise istemci bağlantısı geldiğinde sunucu meşgulse beklemeye alınacak maksimum istemci bağlantı sayısını belirler. |
soket tarafından kullanılan dinleme bağlantı noktası numarasını döndürür | |
soketin bağlı olduğu yerel adresi döndürür | |
sunucuyu bağlantı bekleme moduna geçirir (engelleyici işlem). Bir istemci bağlantısı geldiğinde, istemciye hizmetin sunulacağı bir soket döndürür. | |
soketi ve G/Ç akışlarını kapatır | |
soketi "temsil eden" bir karakter dizisi döndürür | |
hizmet soketini kapatır ve ona bağlı kaynakları serbest bırakır |
8.3.7.2. Hizmetin açılması
Aşağıdaki iki yapıcı ile gerçekleştirilir:
port, hizmetin dinleme bağlantı noktasıdır: istemcilerin bağlantı isteklerini gönderdikleri bağlantı noktasıdır. count, hizmetin kuyruk kapasitesinin maksimum değeridir (varsayılan olarak 50); bu kuyruk, sunucunun henüz yanıtlamadığı istemcilerin bağlantı isteklerini depolar. Kuyruk dolduğunda, gelen bağlantı istekleri reddedilir. Her iki yapı da bir istisna oluşturur.
8.3.7.3. Bağlantı isteğinin kabul edilmesi
Bir istemci, hizmetin dinleme bağlantı noktası üzerinden bir bağlantı isteği gönderdiğinde, hizmet bu isteği şu yöntemle kabul eder:
Bu yöntem, Socket nesnesini döndürür: bu, hizmetin sunulacağı sokettir; hizmet genellikle başka bir görev tarafından sunulur. Yöntem bir istisna oluşturabilir.
8.3.7.4. Hizmet soketi üzerinden okuma/yazma
Hizmet soketi, Socket sınıfının bir örneği olduğundan, bu konunun ele alındığı önceki bölümlere başvurulmalıdır.
8.3.7.5. Müşteriyi tanımlama
Hizmet soketi elde edildikten sonra, müşteri şu yöntemle tanımlanabilir:
yöntemiyle tanımlanabilir. Böylece IP adresine ve müşterinin adına erişilebilir.
8.3.7.6. Hizmeti kapatma
Bu işlem,
yöntemiyle gerçekleştirilir. Bu işlem, kullanılan kaynakları, özellikle de dinleme bağlantı noktasını serbest bırakır. Yöntem bir istisna oluşturabilir.
8.3.7.7. Bir sunucunun temel mimarisi
Yukarıda anlatılanlardan yola çıkarak, bir sunucunun temel yapısını şu şekilde yazabiliriz:
SocketServer sEcoute=null;
try{
// hizmet açılıyor
int portEcoute=…
int maxConnexions=…
sEcoute=new ServerSocket(portEcoute,maxConnexions);
// bağlantı istekleri işleniyor
boolean fini=false;
Socket sService=null;
while( ! fini){
// bir talebin bekletilmesi ve kabulü
sService=sEcoute.accept();
// hizmet, hizmet soketinin aktarıldığı başka bir görev tarafından sunulur
new Service(sService).start();
// bağlantı taleplerini bekleme durumuna geri dönülür
}
// işlem tamamlandı - hizmet sonlandırılıyor
sEcoute.close();
} catch (Exception e){
// istisna işlenir
…
}
Service sınıfı, aşağıdaki gibi görünebilecek bir thread'tir:
public class Service extends Thread{
Socket sService; // hizmet soketi
// yapıcı
public Service(Socket S){
sService=S;
}
// çalıştırma
public void run(){
try{
// giriş-çıkış akışları oluşturuluyor
BufferedReader in=new BufferedReader(new InputStreamReader(sService.getInputStream()));
PrinttWriter out=new PrintWriter(sService.getOutputStream(),true);
// istek-yanıt döngüsü
boolean fini=false;
String demande;
String réponse;
while (! fini){
// isteği okunur
demande=in.readLine();
// işleniyor
…
// yanıt hazırlanıyor
réponse=…
// yanıt gönderiliyor
out.println(réponse);
}
// işlem tamamlandı
sService.close();
} catch(Exception e){
// istisna işleniyor
….
}// try
} // çalıştır
8.4. Uygulamalar
8.4.1. Yankı Sunucusu
DOS penceresinden şu komutla başlatılacak bir yankı sunucusu yazmayı planlıyoruz:
Sunucu, parametre olarak geçirilen bağlantı noktasında çalışır. Sunucu, istemcinin kendisine gönderdiği isteği, istemcinin kimliği (IP+ad) ile birlikte istemciye geri gönderir. Bekleme kuyruğunda 2 bağlantı kabul eder. Burada bir TCP sunucusunun tüm bileşenleri mevcuttur. Program şu şekildedir:
// çağrı: serveurEcho bağlantı noktası
// yankı sunucusu
// istemciye, istemcinin kendisine gönderdiği satırı geri gönderir
import java.net.*;
import java.io.*;
public class serveurEcho{
public final static String syntaxe="Syntaxe : serveurEcho port";
public final static int nbConnexions=2;
// ana program
public static void main (String arg[]){
// bir argüman var mı
if(arg.length != 1)
erreur(syntaxe,1);
// bu argüman >0 bir tamsayı olmalıdır
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// dinleme soketi oluşturulur
ServerSocket ecoute=null;
try{
ecoute=new ServerSocket(port,nbConnexions);
} catch (Exception e){
erreur("Erreur lors de la création de la socket d'écoute ("+e+")",3);
}
// izleme
System.out.println("Serveur d'écho lancé sur le port " + port);
// hizmet döngüsü
boolean serviceFini=false;
Socket service=null;
while (! serviceFini){
// bir istemci bekleniyor
try{
service=ecoute.accept();
} catch (IOException e){
erreur("Erreur lors de l'acceptation d'une connexion ("+e+")",4);
}
// bağlantı tanımlanıyor
try{
System.out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// hizmet başka bir görev tarafından sağlanıyor
new traiteClientEcho(service).start();
}// while döngüsünün sonu
}// main sonu
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// tanımlama
private static String identifie(InetAddress Host){
// ana bilgisayar tanımlama
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// class sonu
// yankı sunucusunun bir müşterisine hizmet sağlar
class traiteClientEcho extends Thread{
private Socket service; // hizmet soketi
private BufferedReader in; // giriş akışı
private PrintWriter out; // çıkış akışı
// yapıcı
public traiteClientEcho(Socket service){
this.service=service;
}
// run yöntemi
public void run(){
// giriş ve çıkış akışlarının oluşturulması
try{
in=new BufferedReader(new InputStreamReader(service.getInputStream()));
} catch (IOException e){
erreur("Erreur lors de la création du flux déentrée de la socket de service ("+e+")",1);
}// try sonu
try{
out=new PrintWriter(service.getOutputStream(),true);
} catch (IOException e){
erreur("Erreur lors de la création du flux de sortie de la socket de service ("+e+")",1);
}// try sonu
// bağlantı kimliği istemciye gönderilir
try{
out.println("Client ["+identifie(service.getInetAddress())+","+
service.getPort()+"] connecté au serveur [" + identifie (InetAddress.getLocalHost())
+ "," + service.getLocalPort() + "]");
} catch (Exception e) {
erreur("identification liaison",1);
}
// istek/yazma-yanıt okuma döngüsü
String demande,reponse;
try{
// istemci dosya sonu işaretini gönderdiğinde hizmet durur
while ((demande=in.readLine())!=null){
// isteğin yankısı
reponse="["+demande+"]";
out.println(reponse);
// istemci "son" gönderdiğinde hizmet durur
if(demande.trim().toLowerCase().equals("fin")) break;
}// while döngüsünün sonu
} catch (IOException e){
erreur("Erreur lors des échanges client/serveur ("+e+")",3);
}// try sonu
// soket kapatılıyor
try{
service.close();
} catch (IOException e){
erreur("Erreur lors de la fermeture de la socket de service ("+e+")",2);
}// try sonu
}// çalıştırma sonu
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}// hata sonu
// kimlik belirleme
private String identifie(InetAddress Host){
// ana bilgisayar tanımlama
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// sınıf sonu
Hizmet için gerekli olan iki sınıf, tek bir kaynak dosyada birleştirilmiştir. Bunlardan yalnızca biri, yani main işlevine sahip olan sınıf, public özniteliğine sahiptir. Sunucunun yapısı, genel TCP sunucu mimarisine uygundur. Sunucu ile bir istemci arasındaki bağlantıyı tanımlamaya yarayan bir yöntem (identifie) eklenmiştir. İşte bazı sonuçlar:
Sunucu şu komutla başlatılır:
Ardından kontrol penceresinde şu mesaj görüntülenir:
Bu sunucuyu test etmek için, hem Unix hem de Windows’ta bulunan telnet programı kullanılır. Telnet, iletişimlerinde satır sonu işaretiyle biten metin satırlarını kabul eden tüm sunuculara uygun, evrensel bir TCP istemcisidir. Echo sunucumuz da buna bir örnektir. Windows (bu örnekte Windows 2000) üzerinde, bir DOS penceresine telnet yazarak ilk telnet istemcisini başlatıyoruz:
DOS>telnet
Microsoft (R) Windows 2000 (TM) version 5.00 (numéro 2195)
Client Telnet Microsoft
Client Telnet numéro 5.00.99203.1
Le caractère d'échappement est 'CTRL+$'
Microsoft Telnet> help
Les commandes peuvent être abrégées. Les commandes prises en charge sont :
close ferme la connexion en cours
display affiche les paramètres d'opération
open ouvre une connexion à un site
quit quitte telnet
set définit les options (entrez 'set ?' pour afficher la liste)
status affiche les informations d'état
unset annule les options (entrez 'unset ?' pour afficher la liste)
? ou help affiche des informations d'aide
Microsoft Telnet> set ?
NTLM Active l'authentification NTLM.
LOCAL_ECHO Active l'écho local.
TERM x (où x est ANSI, VT100, VT52 ou VTNT))
CRLF Envoi de CR et de LF
Microsoft Telnet> set local_echo
Microsoft Telnet> open localhost 187
telnet programı, varsayılan olarak klavyeden girilen komutları ekrana yansıtmaz. Bu yansıma özelliğini etkinleştirmek için şu komutu girin:
Sunucuyla bağlantı kurmak için, yankı hizmetinin bağlantı noktasını (187) ve sunucunun bulunduğu makinenin adresini (localhost) belirterek şu komutu verin:
İstemcinin DOS penceresinde şu mesaj görüntülenir:
Sunucu penceresinde şu mesaj görüntülenir:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [127.0.0.1,tahe,187]
Burada tahe ve localhost aynı makineyi ifade eder. telnet istemci penceresinde metin satırları girebiliriz. Sunucu bunları yankı olarak geri gönderir:
Client [127.0.0.1,tahe,1059] connectÚ au serveur [127.0.0.1,tahe,187]
je suis là
[je suis là]
au revoir
[au revoir]
İstemcinin bağlantı noktası (1059) doğru bir şekilde algılanmış olsa da, hizmet bağlantı noktası (187) dinleme bağlantı noktasıyla (187) aynıdır; bu beklenmedik bir durumdur. Aslında, dinleme bağlantı noktası yerine hizmet soketinin bağlantı noktasını elde etmemiz beklenirdi. Unix altında da aynı sonuçların elde edilip edilmediğini kontrol etmek gerekir. Şimdi ikinci bir istemciyi çalıştıralım: telnet. Sunucu penceresi şu şekilde görünür:
Serveur d'écho lancé sur le port 187
Client [127.0.0.1,tahe,1059] connecté au serveur [127.0.0.1,tahe,187]
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
İkinci istemcinin penceresine de metin satırları girebiliriz:
Client [127.0.0.1,tahe,1060] connecté au serveur [127.0.0.1,tahe,187]
ligne1
[ligne1]
ligne2
[ligne2]
Böylece, yankı sunucusunun aynı anda birden fazla istemciye hizmet verebileceği görülmektedir. telnet istemcileri, çalıştıkları DOS penceresi kapatılarak sonlandırılabilir.
8.4.2. Yankı sunucusu için bir Java istemcisi
Önceki bölümde, yankı hizmetini test etmek için bir telnet istemcisi kullandık. Şimdi kendi istemcimizi yazıyoruz:
// çağrı: clientEcho makine bağlantı noktası
// yankı sunucusunun istemcisi
// satırları sunucuya gönderir, sunucu da bunları yankılar
import java.net.*;
import java.io.*;
public class clientEcho{
public final static String syntaxe="Syntaxe : clientEcho machine port";
// ana program
public static void main (String arg[]){
// iki argüman var mı
if(arg.length != 2)
erreur(syntaxe,1);
// ilk argüman mevcut bir makinenin adı olmalıdır
String machine=arg[0];
InetAddress serveurAddress=null;
try{
serveurAddress=InetAddress.getByName(machine);
} catch (Exception e){
erreur(syntaxe+"\nMachine "+machine+" inaccessible (" + e +")",2);
}
// bağlantı noktası >0 olan bir tamsayı olmalıdır
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(arg[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\nPort incorrect ("+E+")",3);
// sunucuya bağlanılıyor
Socket sClient=null;
try{
sClient=new Socket(machine,port);
} catch (Exception e){
erreur("Erreur lors de la création de la socket de communication ("+e+")",4);
}
// bağlantı tanımlanıyor
try{
System.out.println("Client : Client ["+identifie(InetAddress.getLocalHost())+","+
sClient.getLocalPort()+"] connecté au serveur [" + identifie (sClient.getInetAddress())
+ "," + sClient.getPort() + "]");
} catch (Exception e) {
erreur("identification liaison ("+e+")",5);
}
// klavyeden girilen satırların okuma akışı oluşturuluyor
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
erreur("Création du flux d'entrée clavier ("+e+")",6);
}
// istemci soketiyle ilişkili giriş akışının oluşturulması
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(sClient.getInputStream()));
} catch (Exception e){
erreur("Création du flux d'entrée de la socket client("+e+")",7);
}
// istemci soketiyle ilişkili çıkış akışının oluşturulması
PrintWriter out=null;
try{
out=new PrintWriter(sClient.getOutputStream(),true);
} catch (Exception e){
erreur("Création du flux de sortie de la socket ("+e+")",8);
}
// istek-yanıt döngüsü
boolean serviceFini=false;
String demande=null;
String reponse=null;
// bağlantı kurulduktan hemen sonra sunucu tarafından gönderilen mesajın okunması
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// yanıtın görüntülenmesi
System.out.println("Serveur : " +reponse);
while (! serviceFini){
// klavyeden girilen bir satırın okunması
System.out.print("Client : ");
try{
demande=IN.readLine();
} catch (Exception e){
erreur("Lecture ligne ("+e+")",9);
}
// ağa istek gönderme
try{
out.println(demande);
} catch (Exception e){
erreur("Envoi demande ("+e+")",10);
}
// yanıtı bekleme/okuma
try{
reponse=in.readLine();
} catch (IOException e){
erreur("Lecture réponse ("+e+")",4);
}
// yanıtın görüntülenmesi
System.out.println("Serveur : " +reponse);
// bitti mi?
if(demande.trim().toLowerCase().equals("fin")) serviceFini=true;
}
// bitti
try{
sClient.close();
} catch(Exception e){
erreur("Fermeture socket ("+e+")",11);
}
}// ana sayfa
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
// kimlik
private static String identifie(InetAddress Host){
// Ana Bilgisayar Kimliği
String ipHost=Host.getHostAddress();
String nomHost=Host.getHostName();
String idHost;
if (nomHost == null) idHost=ipHost;
else idHost=ipHost+","+nomHost;
return idHost;
}
}// sınıf sonu
Bu istemcinin yapısı, tcp istemcilerinin genel mimarisine uygundur. Burada, olası farklı istisnalar tek tek ele alınmıştır, bu da programı ağırlaştırmaktadır. Bu istemciyi test ettiğimizde elde edilen sonuçlar şunlardır:
Client : Client [127.0.0.1,tahe,1045] connecté au serveur [127.0.0.1,localhost,187]
Serveur : Client [127.0.0.1,localhost,1045] connectÚ au serveur [127.0.0.1,tahe,187]
Client : 123
Serveur : [123]
Client : abcd
Serveur : [abcd]
Client : je suis là
Serveur : [je suis là]
Client : fin
Serveur : [fin]
Client ile başlayan satırlar istemci tarafından gönderilen satırlardır; Serveur ile başlayan satırlar ise sunucu tarafından yankılanan satırlardır.
8.4.3. Genel bir TCP istemcisi
İnternetin ilk dönemlerinde oluşturulan birçok hizmet, daha önce incelediğimiz yankı sunucusu modeline göre çalışır: istemci-sunucu iletişimi, metin satırlarının karşılıklı olarak gönderilmesi yoluyla gerçekleşir. Aşağıdaki şekilde başlatılacak genel bir TCP istemcisi yazacağız: java cltTCPgenerique sunucu bağlantı noktası
Bu TCP istemcisi, serveur sunucusunun port bağlantı noktasına bağlanacaktır. Bağlantı kurulduktan sonra, iki iş parçacığı oluşturacaktır:
- klavyeden girilen komutları okumak ve sunucuya göndermekle görevli bir iş parçacığı
- sunucudan gelen yanıtları okumak ve bunları ekranda görüntülemekle görevli bir iş parçacığı
Önceki uygulamada böyle bir ihtiyaç duyulmamışken neden iki iş parçacığı gerekiyor? Önceki uygulamada iletişim protokolü belliydi: istemci tek bir satır gönderir, sunucu da tek bir satırla yanıt verirdi. Her hizmetin kendine özgü bir protokolü vardır ve aşağıdaki durumlarla da karşılaşılabilir:
- istemcinin yanıt alabilmesi için birkaç satır metin göndermesi gerekir
- sunucunun yanıtı birden fazla satır metin içerebilir
Bu nedenle, sunucuya tek bir satır gönderme - sunucudan gönderilen tek bir satırı alma döngüsü her zaman uygun olmayabilir. Dolayısıyla, birbirinden bağımsız iki döngü oluşturacağız:
- klavyeden girilen ve sunucuya gönderilecek komutları okuma döngüsü. Kullanıcı, fin anahtar kelimesiyle komutların bittiğini bildirecektir.
- sunucudan gelen yanıtları alıp görüntüleyen bir döngü. Bu, sunucu tarafından ağ akışının kapatılmasıyla veya kullanıcı tarafından klavyeden fin komutunun girilmesiyle sonlandırılacak sonsuz bir döngü olacaktır.
Bu iki döngüyü birbirinden ayırmak için iki bağımsız iş parçacığına ihtiyacımız var. Genel tcp istemcimizin SMTP (SendMail Aktarım Protokolü) hizmetine bağlandığı bir yürütme örneğini inceleyelim. Bu hizmet, e-postaların alıcılara iletilmesinden sorumludur. 25 numaralı bağlantı noktasında çalışır ve metin satırları alışverişi türünde bir iletişim protokolüne sahiptir.
Dos>java clientTCPgenerique istia.univ-angers.fr 25
Commandes :
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test
ligne1
ligne2
ligne3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Bu istemci-sunucu iletişimlerini inceleyelim:
- SMTP hizmeti, bir istemci ona bağlandığında bir hoş geldiniz mesajı gönderir:
- Bazı hizmetlerde, hizmetle kullanılabilecek komutlar hakkında bilgi veren help komutu bulunur. Burada durum böyle değildir. Örnekte kullanılan SMTP komutları şunlardır:
- mail from: expéditeur, mesajın göndereninin e-posta adresini belirtmek için
- rcpt to: destinataire, mesajın alıcısının e-posta adresini belirtmek için kullanılır. Birden fazla alıcı varsa, her alıcı için rcpt to: komutu gerektiği kadar tekrar edilir.
- data, SMTP sunucusuna mesajın gönderileceğini bildirir. Sunucunun yanıtında belirtildiği gibi, bu mesaj, sadece nokta karakterini içeren bir satırla biten bir dizi satırdan oluşur. Bir mesajın, mesaj gövdesinden boş bir satırla ayrılmış başlıkları olabilir. Örneğimizde, Subject: anahtar kelimesini kullanarak bir konu başlığı ekledik:
- Mesaj gönderildikten sonra, quit komutuyla sunucuya işin bittiğini bildirebiliriz. Sunucu daha sonra ağ bağlantısını kapatır. Okuma iş parçacığı bu olayı algılayabilir ve durabilir.
- Kullanıcı, klavyeden "fin" yazarak klavyeden girilen komutları okuma iş parçacığını da durdurabilir.
Alınan postayı kontrol edersek, şunu görürüz (Outlook):

SMTP hizmetinin, bir gönderenin geçerli olup olmadığını algılayamadığı dikkatinizi çekecektir. Bu nedenle, bir iletinin from alanına asla güvenilemez. Burada machin@univ-angers.fr göndereni mevcut değildi.
Bu genel tcp istemcisi, internet hizmetlerinin iletişim protokolünü keşfetmemizi ve buradan yola çıkarak bu hizmetlerin istemcileri için özel sınıflar oluşturmamızı sağlayabilir. Bir sunucuda depolanan e-postaları bulmamızı sağlayan POP hizmetinin (Post Office Protocol) iletişim protokolünü inceleyelim. Bu hizmet 110 numaralı bağlantı noktasında çalışır.
Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commandes :
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass monpassword
<-- +OK st has 157 visible messages (0 hidden) in 11755927 octets.
list
<-- +OK 157 visible messages (11755927 octets)
<-- 1 892847
<-- 2 171661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 octets
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<-- by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<-- Mon, 13 May 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<-- by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<-- Mon, 13 May 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2 Tl. : 0800 77 47 95
<-- Fax : (+33) 01 40 78 64 00 , Email : noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Başlıca komutlar şunlardır:
- user login; burada, e-postalarımızın bulunduğu sunucudaki kullanıcı adımızı gireriz
- password şifresini girin; burada, önceki oturum açma bilgisine ait şifre girilir
- list, mesajların numara ve bayt cinsinden boyut bilgilerini içeren listesini almak için
- retr i, i numaralı mesajı okumak için
- quit, diyaloğu sonlandırmak için.
Şimdi, genellikle 80 numaralı bağlantı noktasında çalışan bir web sunucusu ile istemci arasındaki iletişim protokolünü inceleyelim:
Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commandes :
GET /index.html HTTP/1.0
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Bienvenue a l'ISTIA - Universite d'Angers</title>
<-- </head>
....
<-- face="Verdana"> - Dernire mise jour le <b>10 janvier 2002</b></font></p>
<-- </body>
<-- </html>
<--
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Bir web istemcisi, komutlarını aşağıdaki şemaya göre sunucuya gönderir:
Web sunucusu ancak boş satırı aldıktan sonra yanıt verir. Örnekte sadece bir komut kullandık:
Bu komut, sunucudan URL /index.html dosyasını ister ve HTTP protokolünün 1.0 sürümüyle çalıştığını belirtir. Bu protokolün en güncel sürümü 1.1'dir. Örnekte, sunucunun index.html dosyasının içeriğini geri göndererek yanıt verdiği ve ardından yanıt okuma iş parçacığının sona erdiği görüldüğünden bağlantıyı kapattığı görülmektedir. index.html dosyasının içeriğini göndermeden önce, web sunucusu boş bir satırla biten bir dizi başlık göndermiştir:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<html> satırı, /index.html dosyasının ilk satırıdır. Yukarıdakiler, HTTP (HyperText Aktarım Protokolü) başlıkları olarak adlandırılır. Bu başlıkları burada ayrıntılı olarak ele almayacağız, ancak genel amaçlı istemcimizin bunlara erişim sağladığını hatırlayalım; bu, başlıkları anlamak açısından yararlı olabilir. Örneğin ilk satır:
, erişilen web sunucusunun HTTP/1.1 protokolünü anladığını ve istenen dosyayı (200 OK) başarıyla bulduğunu gösterir; burada 200, bir HTTP yanıt kodudur. Şu satırlar:
satırları, istemciye HTML (HyperText İşaretleme Dili) metnini temsil eden 11251 bayt alacağını ve gönderimin sonunda bağlantının kapatılacağını bildirir.
Dolayısıyla burada çok kullanışlı bir TCP istemcisiyle karşı karşıyayız. Şüphesiz daha önce kullandığımız telnet programından daha az işlev görüyor, ancak bunu kendimiz yazmak ilginçti. Genel amaçlı TCP istemci programı şöyledir:
// ithal edilen paketler
import java.io.*;
import java.net.*;
public class clientTCPgenerique{
// bir hizmetin özelliklerini şu biçimde parametre olarak alır
// sunucu bağlantı noktası
// hizmete bağlanır
// klavyeden girilen komutları okumak için bir iş parçacığı oluşturur
// bu komutlar sunucuya gönderilir
// sunucudan gelen yanıtları okumak için bir iş parçacığı oluşturur
// bu yanıtlar ekranda görüntülenecek
// tüm işlem, klavyeden girilen "fin" komutuyla sona erer
// örnek değişkeni
private static Socket client;
public static void main(String[] args){
// sözdizimi
final String syntaxe="pg serveur port";
// argüman sayısı
if(args.length != 2)
erreur(syntaxe,1);
// sunucu adı not edilir
String serveur=args[0];
// bağlantı noktası >0 olan bir tamsayı olmalıdır
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[1]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
client=null;
// sorunlar yaşanabilir
try{
// hizmete bağlanılır
client=new Socket(serveur,port);
}catch(Exception ex){
// hata
erreur("Impossible de se connecter au service ("+ serveur
+","+port+"), erreur : "+ex.getMessage(),3);
// son
return;
}//yakalama
// okuma/yazma iş parçacıkları oluşturuluyor
new ClientSend(client).start();
new ClientReceive(client).start();
// ana iş parçacığı sonu
return;
}// ana iş parçacığı
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
// hata görüntüleme
System.err.println(msg);
// hata ile durdurma
System.exit(exitCode);
}//hata
}//sınıf
class ClientSend extends Thread {
// klavyeden girilen komutları okumakla görevli sınıf
// ve bunları parametre olarak geçirilen bir TCP istemcisi aracılığıyla sunucuya göndermekle görevli sınıf
private Socket client; // TCP istemcisi
// yapıcı
public ClientSend(Socket client){
// burada TCP istemcisi belirtilir
this.client=client;
}//oluşturucu
// iş parçacığının Run yöntemi
public void run(){
// yerel veriler
PrintWriter OUT=null; // ağ yazma akışı
BufferedReader IN=null; // klavye akışı
String commande=null; // klavyeden okunan komut
// hata yönetimi
try{
// ağ yazma akışının oluşturulması
OUT=new PrintWriter(client.getOutputStream(),true);
// klavye giriş akışının oluşturulması
IN=new BufferedReader(new InputStreamReader(System.in));
// komut girişi-gönderme döngüsü
System.out.println("Commandes : ");
while(true){
// klavyeden girilen komutun okunması
commande=IN.readLine().trim();
// bitti mi?
if (commande.toLowerCase().equals("fin")) break;
// komutu sunucuya gönderme
OUT.println(commande);
// sonraki komut
}//while
}catch(Exception ex){
// hata
System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
}//catch
// son - akışlar kapatılıyor
try{
OUT.close();client.close();
}catch(Exception ex){}
// iş parçacığının sonu bildiriliyor
System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
}//çalıştır
}//sınıf
class ClientReceive extends Thread{
// bir
// parametre olarak geçirilen bir TCP istemcisine yönelik metin satırlarını okumakla görevli sınıf
private Socket client; // TCP istemcisi
// yapıcı
public ClientReceive(Socket client){
// TCP istemcisi kaydedilir
this.client=client;
}//oluşturucu
// iş parçacığının Run yöntemi
public void run(){
// yerel veriler
BufferedReader IN=null; // ağ okuma akışı
String réponse=null; // sunucu yanıtı
// hata yönetimi
try{
// ağ okuma akışının oluşturulması
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// IN akışındaki metin satırlarını okuma döngüsü
while(true){
// ağ akışını okuma
réponse=IN.readLine();
// akış kapalı mı?
if(réponse==null) break;
// görüntüleme
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// hata
System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
}//yakalama
// son - akışlar kapatılıyor
try{
IN.close();client.close();
}catch(Exception ex){}
// iş parçacığının sonu bildiriliyor
System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
}//çalıştır
}//sınıf
8.4.4. Genel bir TCP sunucusu
Şimdi,
- bu sunucu, istemcilerinden gelen komutları ekrana yansıtır
- ve yanıt olarak bir kullanıcının klavyeden girdiği metin satırlarını onlara gönderen bir sunucuya odaklanacağız. Dolayısıyla sunucu rolünü üstlenen kişi kullanıcıdır.
Program şu komutla çalıştırılır: java serveurTCPgenerique portEcoute; burada portEcoute, istemcilerin bağlanması gereken bağlantı noktasıdır. İstemciye hizmet, iki iş parçacığı tarafından sağlanacaktır:
- bir iş parçacığı, yalnızca istemci tarafından gönderilen metin satırlarını okumaya adanmıştır
- diğeri ise yalnızca kullanıcının klavyeden girdiği yanıtları okumaya adanmış bir iş parçacığıdır. Bu iş parçacığı, fin komutuyla istemciyle olan bağlantıyı sonlandıracağını bildirir.
Sunucu, her müşteri için iki iş parçacığı oluşturur. n müşteri varsa, aynı anda 2n aktif iş parçacığı olacaktır. Sunucu, kullanıcı tarafından klavyeden Ctrl-C tuşlarına basılmadıkça asla durmaz. Birkaç örneğe bakalım.
Sunucu 100 numaralı bağlantı noktasında çalıştırılır ve sunucuyla iletişim kurmak için genel amaçlı istemci kullanılır. İstemci penceresi şöyledir:
E:\data\serge\MSNET\c#\ağ\genel tcp istemcisi> java clientTCPgenerique localhost 100
Commandes :
commande 1 du client 1
<-- réponse 1 au client 1
commande 2 du client 1
<-- réponse 2 au client 1
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
<-- ile başlayan satırlar sunucudan istemciye gönderilenler, diğerleri ise istemciden sunucuya gönderilenlerdir. Sunucu penceresi şu şekildedir:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
<-- ile başlayan satırlar, istemciden sunucuya gönderilen satırlardır. N: ile başlayan satırlar, sunucudan N numaralı istemciye gönderilen satırlardır. Yukarıdaki sunucu hâlâ aktifken, istemci 1 işlemi tamamlanmıştır. Aynı sunucu için ikinci bir istemci başlatılır:
Dos> java clientTCPgenerique localhost 100
Commandes :
commande 3 du client 2
<-- réponse 3 au client 2
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]
Sunucu penceresi şu şekildedir:
Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- commande 3 du client 2
réponse 3 au client 2
2 : [fin du Thread de lecture des demandes du client 2]
fin
[fin du Thread de lecture des réponses du serveur au client 2]
^C
Şimdi, genel sunucumuzu 88 numaralı bağlantı noktasında başlatarak bir web sunucusu simülasyonu yapalım:
Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Şimdi bir tarayıcı açalım ve URL adresindeki http://localhost:88/exemple.html sayfasını isteyelim. Tarayıcı, localhost makinesinin 88 bağlantı noktasına bağlanacak ve ardından /exemple.html sayfasını isteyecektir:

Şimdi sunucumuzun penceresine bakalım:
Dos>java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
Böylece tarayıcı tarafından gönderilen HTTP başlıklarını görüyoruz. Bu, HTTP protokolünü yavaş yavaş keşfetmemizi sağlıyor. Önceki bir örnekte, yalnızca GET komutunu gönderen bir web istemcisi oluşturmuştuk. Bu yeterli olmuştu. Burada tarayıcının sunucuya başka bilgiler de gönderdiğini görüyoruz. Bu bilgilerin amacı, sunucuya karşısındaki istemcinin türünü belirtmektir. Ayrıca HTTP başlıklarının boş bir satırla bittiğini de görüyoruz.
Müşterimize bir yanıt hazırlayalım. Burada klavyeyi kullanan kullanıcı asıl sunucudur ve yanıtı elle hazırlayabilir. Önceki bir örnekte bir web sunucusu tarafından verilen yanıtı hatırlayalım:
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix) (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
Benzer bir cevap vermeye çalışalım:
...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2 : HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
2 : fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du Thread de lecture des demandes du client 2]
[fin du Thread de lecture des réponses du serveur au client 2]
2: ile başlayan satırlar, sunucudan 2 numaralı istemciye gönderilir. fin komutu, sunucuyla istemci arasındaki bağlantıyı sonlandırır. Yanıtımızda aşağıdaki HTTP başlıklarıyla sınırlı kaldık:
HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
Göndereceğimiz dosyanın boyutunu (Content-Length) belirtmiyoruz, ancak dosyayı gönderdikten sonra bağlantıyı kapatacağımızı (Connection: close) söylüyoruz. Bu, tarayıcı için yeterlidir. Bağlantının kapandığını görünce, tarayıcı sunucudan gelen yanıtın tamamlandığını anlayacak ve kendisine gönderilen HTML sayfasını görüntüleyecektir. Bu sayfa şöyledir:
2 : <html>
2 : <head><title>Serveur generique</title></head>
2 : <body>
2 : <center>
2 : <h2>Reponse du serveur generique</h2>
2 : </center>
2 : </body>
2 : </html>
Kullanıcı daha sonra fin komutunu girerek istemciyle olan bağlantıyı kapatır. Tarayıcı, sunucudan gelen yanıtın tamamlandığını anlar ve yanıtı görüntüleyebilir:

Yukarıdaki örnekte, tarayıcının ne aldığını görmek için View/Source komutunu girersek şu sonucu alırız:

yani genel sunucudan gönderdiğimizin aynısı.
Genel TCP sunucusunun kodu şöyledir:
// paketler
import java.io.*;
import java.net.*;
public class serveurTCPgenerique{
// ana program
public static void main (String[] args){
// dinleme bağlantı noktasını alır ve istemcilerin isteklerini dinler
// istemcilerin isteklerini okumak için bir iş parçacığı oluşturur
// bu istekler ekranda görüntülenecek
// klavyeden girilen komutları okumak için bir iş parçacığı oluşturur
// bunlar müşteriye yanıt olarak gönderilecektir
// tüm işlem, klavyeden girilen "fin" komutuyla sona erer
final String syntaxe="Syntaxe : pg port";
// örnek değişkeni
// bir argüman var mı
if(args.length != 1)
erreur(syntaxe,1);
// bağlantı noktası >0 olan bir tamsayı olmalıdır
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// dinleme hizmeti oluşturulur
ServerSocket ecoute=null;
int nbClients=0; // işlenen istemci sayısı
try{
// hizmet oluşturuluyor
ecoute=new ServerSocket(port);
// izleme
System.out.println("Serveur générique lancé sur le port " + port);
// müşterilere hizmet döngüsü
Socket client=null;
while (true){ // sonsuz döngü - Ctrl-C ile durdurulur
// bir istemciyi bekliyor
client=ecoute.accept();
// hizmet, ayrı iş parçacıklarıyla sağlanır
nbClients++;
// okuma/yazma iş parçacıkları oluşturuluyor
new ServeurSend(client,nbClients).start();
new ServeurReceive(client,nbClients).start();
// istekleri dinlemeye geri dönülüyor
}// while döngüsünün sonu
}catch(Exception ex){
// hata bildirilir
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}// main sonu
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
// hatanın görüntülenmesi
System.err.println(msg);
// hata ile durdurma
System.exit(exitCode);
}//hata
}//sınıf
class ServeurSend extends Thread{
// klavyeden girilen yanıtları okumakla görevli sınıf
// ve bunları yapıcıya aktarılan bir TCP istemcisi aracılığıyla bir istemciye göndermekle görevli sınıf
Socket client; // TCP istemcisi
int numClient; // istemci numarası
// yapıcı
public ServeurSend(Socket client, int numClient){
// burada TCP istemcisi not edilir
this.client=client;
// ve numarası
this.numClient=numClient;
}//üretici
// iş parçacığının Run yöntemi
public void run(){
// yerel veriler
PrintWriter OUT=null; // ağ yazma akışı
String réponse=null; // klavyeden okunan yanıt
BufferedReader IN=null; // klavye akışı
// izleme
System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
// hata yönetimi
try{
// ağ yazma akışının oluşturulması
OUT=new PrintWriter(client.getOutputStream(),true);
// klavye akışının oluşturulması
IN=new BufferedReader(new InputStreamReader(System.in));
// komut girişi-gönderme döngüsü
while(true){
// müşteri kimlik doğrulaması
System.out.print("--> " + numClient + " : ");
// klavyeden girilen yanıtın okunması
réponse=IN.readLine().trim();
// bitti mi?
if (réponse.toLowerCase().equals("fin")) break;
// yanıtın sunucuya gönderilmesi
OUT.println(réponse);
// sonraki yanıt
}//while
}catch(Exception ex){
// hata
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//yakalama
// son - akışlar kapatılıyor
try{
OUT.close();client.close();
}catch(Exception ex){}
// iş parçacığının sonu bildiriliyor
System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
}//çalıştır
}//sınıf
class ServeurReceive extends Thread{
// sunucuya gönderilen metin satırlarını okumakla görevli sınıf
// yapıcıya aktarılan bir TCP istemcisi aracılığıyla
Socket client; // TCP istemcisi
int numClient; // istemci numarası
// yapıcı
public ServeurReceive(Socket client, int numClient){
// TCP istemcisi not edilir
this.client=client;
// ve numarası
this.numClient=numClient;
}//üretici
// iş parçacığının Run yöntemi
public void run(){
// yerel veriler
BufferedReader IN=null; // ağ okuma akışı
String réponse=null; // sunucu yanıtı
// izleme
System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
// hata yönetimi
try{
// ağ okuma akışının oluşturulması
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
// IN akışındaki metin satırlarını okuma döngüsü
while(true){
// ağ akışını okuma
réponse=IN.readLine();
// akış kapalı mı?
if(réponse==null) break;
// görüntüleme
System.out.println("<-- "+réponse);
}//while
}catch(Exception ex){
// hata
System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
}//yakalama
// son - akışlar kapatılıyor
try{
IN.close();client.close();
}catch(Exception ex){}
// iş parçacığının sonu bildiriliyor
System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
}//çalıştır
}//sınıf
8.4.5. Bir web istemcisi
Önceki örnekte, bir tarayıcının gönderdiği HTTP başlıklarından bazılarını görmüştük:
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
URL parametresini alan ve bu URL'in içeriğini ekranda görüntüleyen bir web istemcisi yazacağız. URL için bağlantı kurulan web sunucusunun HTTP 1.1 protokolünü desteklediğini varsayacağız. Yukarıdaki başlıklardan yalnızca aşağıdakileri kullanacağız:
- İlk başlık, hangi sayfayı istediğimizi belirtir
- ikincisi, hangi sunucuya sorgu gönderdiğimizi
- üçüncüsü ise, sunucunun yanıt verdikten sonra bağlantıyı kapatmasını istediğimizi belirtir.
Yukarıdaki örnekte, GET'i HEAD ile değiştirirsek, sunucu bize sadece HTTP başlıklarını gönderir, HTML sayfasını göndermez.
Web istemcimiz şu şekilde çağrılacaktır: java clientweb URL cmd; burada URL,istenen URL'tir ve cmd, sadece başlıkları (HEAD) mı yoksa sayfanın içeriğini de (GET) mi istediğimizi belirtmek için GET veya HEAD anahtar kelimelerinden biridir (GET). İlk örneğe bir göz atalım. Aynı makinede önce IIS sunucusunu, ardından web istemcisini başlatıyoruz:
dos>java clientweb http://localhost HEAD
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:23:37 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=HMFNCCMDECBJJBPPBHAOAJNP; path=/
Cache-control: private
Cevap
, istenen sayfanın yerinin değiştiği anlamına gelir (yani URL'ten). Yeni adres olan URL, Location: başlığıyla verilir:
Web istemcisine yapılan çağrıda HEAD yerine GET kullanırsak:
dos>java clientweb http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 09:33:36 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=IMFNCCMDAKPNNGMGMFIHENFE; path=/
Cache-control: private
<head><title>L'objet a changé d'emplacement</title></head>
<body><h1>L'objet a changé d'emplacement</h1>Cet objet peut être trouvé <a HREF="/IISSamples/Default/we
lcome.htm">ici</a>.</body>
HEAD ile elde ettiğimiz sonuçla aynı sonucu elde ediyoruz, buna ek olarak HTML sayfasının gövdesi de ekleniyor. Program şu şekildedir:
// ithal edilen paketler
import java.io.*;
import java.net.*;
public class clientweb{
// URL'i ister
// içeriğini ekranda görüntüler
public static void main(String[] args){
// sözdizimi
final String syntaxe="pg URI GET/HEAD";
// argüman sayısı
if(args.length != 2)
erreur(syntaxe,1);
// URI'in talep edildiği belirtilmiştir
String URLString=args[0];
String commande=args[1].toUpperCase();
// URI'in geçerliliğinin kontrolü
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI hatalı
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//yakalama
// siparişin doğrulanması
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// sipariş hatalı
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// URL'ten yararlı bilgiler çıkarılıyor
String path=url.getPath();
if(path.equals("")) path="/";
String query=url.getQuery();
if(query!=null) query="?"+query; else query="";
String host=url.getHost();
int port=url.getPort();
if(port==-1) port=url.getDefaultPort();
// işe devam edilebilir
Socket client=null; // müşteri
BufferedReader IN=null; // müşterinin okuma akışı
PrintWriter OUT=null; // müşterinin yazma akışı
String réponse=null; // sunucunun yanıtı
try{
// sunucuya bağlanıyoruz
client=new Socket(host,port);
// istemcinin giriş-çıkış akışları oluşturuluyor TCP
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// URL isteniyor - başlıkların gönderilmesi HTTP
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// yanıt okunur
while((réponse=IN.readLine())!=null){
// yanıt işlenir
System.out.println(réponse);
}//while
// işlem tamamlandı
client.close();
} catch(Exception e){
// istisna işleniyor
erreur(e.getMessage(),4);
}//catch
}//main
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
// hata görüntüleme
System.err.println(msg);
// hata ile sonlandırma
System.exit(exitCode);
}//hata
}//sınıf
Bu programdaki tek yenilik, URL sınıfının kullanılmasıdır. Program, http://serveur:port/cheminPageHTML?param1=val1;param2=val2;.... biçiminde bir URL (Uniform Resource Locator) veya URI (Uniform Resource Identifier) alır. URL sınıfı, URL dizesini farklı bileşenlerine ayırmamızı sağlar. Bir URL nesnesi, parametre olarak alınan URLstring dizesinden oluşturulur:
// URL'in geçerliliğinin kontrolü
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI hatalı
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//yakalama
Parametre olarak alınan URL dizesi geçerli bir URL değilse (protokol eksikliği, sunucu eksikliği vb.), bir istisna atılır. Bu, alınan parametrenin geçerliliğini kontrol etmemizi sağlar. URL nesnesi oluşturulduktan sonra, bu nesnenin çeşitli öğelerine erişilebilir. Dolayısıyla, önceki koddaki url nesnesi şu dizeden oluşturulmuşsa
dizesinden oluşturulmuşsa, şu sonuç elde edilir:
url.getHost()=serveur
url.getPort()=port veya bağlantı noktası belirtilmediyse -1
url.getPath()=cheminPageHTML veya yol yoksa boş dize
url.getQuery()=param1=val1;param2=val2;... veya istek yoksa null
uri.getProtocol()=http
8.4.6. Yönlendirmeleri yöneten web istemcisi
Önceki web istemcisi, talep ettiği URL'ten olası bir yönlendirmeyi yönetmez. Bir sonraki istemci bunu yönetir.
- Sunucu tarafından gönderilen HTTP başlıklarının ilk satırını okur ve yönlendirmeyi belirten "302 Object moved" dizesinin olup olmadığını kontrol eder
- Sonraki başlıkları okur. Yönlendirme varsa, istenen sayfanın yeni URL adresini veren "Location: url" satırını arar ve bu URL adresini not eder.
- sunucudan gelen yanıtın geri kalanını görüntüler. Yönlendirme varsa, 1'den 3'e kadar olan adımlar yeni URL ile tekrarlanır. Program birden fazla yönlendirmeyi kabul etmez. Bu sınır, değiştirilebilen bir sabit ile belirlenir.
İşte bir örnek:
Dos>java clientweb2 http://localhost GET
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.0
Date: Mon, 13 May 2002 11:38:55 GMT
Connection: close
Location: /IISSamples/Default/welcome.htm
Content-Length: 189
Content-Type: text/html
Set-Cookie: ASPSESSIONIDGQQQGUUY=PDGNCCMDNCAOFDMPHCJNPBAI; path=/
Cache-control: private
<head><title>L'objet a chang d'emplacement</title></head>
<body><h1>L'objet a chang d'emplacement</h1>Cet objet peut tre trouv <a HREF="/IISSamples/Default/we
lcome.htm">ici</a>.</body>
<--Redirection vers l'URL http://localhost:80/IISSamples/Default/welcome.htm-->
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Connection: close
Date: Mon, 13 May 2002 11:38:55 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Mon, 16 Feb 1998 21:16:22 GMT
ETag: "0174e21203bbd1:978"
Content-Length: 4781
<html>
<head>
<title>Bienvenue dans le Serveur Web personnel</title>
</head>
....
</body>
</html>
Program şu şekildedir:
// ithal edilen paketler
import java.io.*;
import java.net.*;
import java.util.regex.*;
public class clientweb2{
// bir URL talep ediyor
// içeriğini ekranda görüntüler
public static void main(String[] args){
// sözdizimi
final String syntaxe="pg URL GET/HEAD";
// argüman sayısı
if(args.length != 2)
erreur(syntaxe,1);
// istenen URI not edilir
String URLString=args[0];
String commande=args[1].toUpperCase();
// URI'in geçerliliğini kontrol eder
URL url=null;
try{
url=new URL(URLString);
}catch (Exception ex){
// URI hatalı
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),2);
}//yakalama
// siparişin doğrulanması
if(! commande.equals("GET") && ! commande.equals("HEAD")){
// sipariş hatalı
erreur("Le second paramètre doit être GET ou HEAD",3);
}
// çalışılabilir
Socket client=null; // müşteri
BufferedReader IN=null; // müşterinin okuma akışı
PrintWriter OUT=null; // müşterinin yazma akışı
String réponse=null; // sunucu yanıtı
final int nbRedirsMax=1; // en fazla bir yönlendirme kabul edilir
int nbRedirs=0; // devam eden yönlendirme sayısı
String premièreLigne; // yanıtın ilk satırı
boolean redir=false; // yönlendirme olup olmadığını gösterir
String locationString=""; // olası bir yönlendirmenin URL dizesi
// yönlendirme dizesi URL'i bulmak için düzenli ifade
Pattern location=Pattern.compile("^Location: (.+?)$");
// hata yönetimi
try{
// Yönlendirmeler varsa, sorgulanacak birden fazla URL olabilir
while(nbRedirs<=nbRedirsMax){
// URL'ten gerekli bilgiler çıkarılır
String protocol=url.getProtocol();
String path=url.getPath();
if(path.equals("")) path="/";
String query=url.getQuery();
if(query!=null) query="?"+query; else query="";
String host=url.getHost();
int port=url.getPort();
if(port==-1) port=url.getDefaultPort();
// sunucuya bağlanılır
client=new Socket(host,port);
// TCP istemcisinin giriş-çıkış akışları oluşturulur
IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
OUT=new PrintWriter(client.getOutputStream(),true);
// URL dosyası istenir - başlıklar gönderilir HTTP
OUT.println(commande + " " + path + query + " HTTP/1.1");
OUT.println("Host: " + host + ":" + port);
OUT.println("Connection: close");
OUT.println();
// yanıtın ilk satırı okunur
premièreLigne=IN.readLine();
// ekran çıktısı
System.out.println(premièreLigne);
// yönlendirme mi?
if(premièreLigne.endsWith("302 Object moved")){
// yönlendirme var
redir=true;
nbRedirs++;
}//if
// başlıklar HTTP başlıkların sonunu belirten boş satır bulunana kadar devam eder
boolean locationFound=false;
while(!(réponse=IN.readLine()).equals("")){
// yanıt görüntülenir
System.out.println(réponse);
// yönlendirme varsa, Location başlığı aranır
if(redir && ! locationFound){
// satırı, location ilişkisel ifadesiyle karşılaştırılır
Matcher résultat=location.matcher(réponse);
if(résultat.find()){
// bulunursa, yönlendirme başlığı olan URL not edilir
locationString=résultat.group(1);
// bulunduğunu not edilir
locationFound=true;
}//if
}//eğer
// aşağıdaki başlık
}// süresince
// yanıtın aşağıdaki satırları
System.out.println(réponse);
while((réponse=IN.readLine())!=null){
// yanıt görüntülenir
System.out.println(réponse);
}//while
// bağlantı kapatılır
client.close();
// işlem tamamlandı mı?
if ( ! locationFound || nbRedirs>nbRedirsMax)
break;
// yönlendirme yapılması gerekiyor - yeni URL oluşturuluyor
URLString=protocol +"://"+host+":"+port+locationString;
url=new URL(URLString);
// takip
System.out.println("\n<--Redirection vers l'URL "+URLString+"-->\n");
}//while
} catch(Exception e){
// istisna işlenir
erreur(e.getMessage(),4);
}//yakalama
}//ana işlev
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
// hata görüntüleme
System.err.println(msg);
// hata ile sonlandırma
System.exit(exitCode);
}//hata
}//sınıf
8.4.7. Vergi Hesaplama Sunucusu
Daha önce çeşitli şekillerde ele aldığımız IMPOTS alıştırmasına geri dönüyoruz. Son versiyonunu hatırlayalım:
Bir temel vergi sınıfı oluşturuldu. Bu sınıfın öznitelikleri, üç sayı tablosundan oluşur:
public class impots{
// vergi hesaplaması için gerekli veriler
// harici bir kaynaktan geliyor
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// üretici alanı boş
protected impots(){}
// oluşturucu
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
impots sınıfının iki yapıcı işlevi vardır:
- verginin hesaplanması için gerekli olan üç veri dizisinin aktarıldığı bir oluşturucu
- yalnızca alt sınıflar tarafından kullanılabilen, parametresiz bir oluşturucu
Bu sınıftan türetilen impotsJDBC sınıfı, bir veritabanının içeriğinden limites, coeffR ve coeffN tablolarını doldurmaya olanak tanır:
public class impotsJDBC extends impots{
// oluşturmaya olanak tanıyan bir oluşturucunun eklenmesi
// tablodan limit, coeffr, coeffn tablolarını oluşturmaya yarayan bir oluşturucu
// bir veritabanındaki vergiler
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS : veritabanının adı DSN
// userIMPOTS, mdpIMPOTS : veritabanına erişim kullanıcı adı/şifresi
Bir grafik uygulaması yazılmıştı. Uygulama, impotsJDBC sınıfına ait bir nesneyi kullanıyordu. Uygulama ve bu nesne aynı makinede bulunuyordu. Test programını ve impotsJDBC nesnesini farklı makinelere yerleştirmeyi planlıyoruz. Böylece, uzak impotsJDBC nesnesinin sunucu görevi göreceği bir istemci-sunucu uygulaması elde edeceğiz. Yeni sınıfın adı ServeurImpots olup, impotsJDBC sınıfından türetilmiştir:
// ithal edilen paketler
import java.net.*;
import java.io.*;
import java.sql.*;
public class ServeurImpots extends impotsJDBC {
// özellikler
int portEcoute; // istemci isteklerini dinleyen bağlantı noktası
boolean actif; // sunucu durumu
// oluşturucu
public ServeurImpots(int portEcoute,String DSNimpots, String USERimpots, String MDPimpots)
throws IOException, SQLException, ClassNotFoundException {
// üst yapı
super(DSNimpots, USERimpots, MDPimpots);
// dinleme bağlantı noktası not edilir
this.portEcoute=portEcoute;
// şu anda etkin değil
actif=false;
// klavyeden girilen komutları okumak için bir iş parçacığı oluşturur ve başlatır
// sunucu bu komutlar aracılığıyla yönetilecektir
Thread admin=new Thread(){
public void run(){
try{
admin();
}catch (Exception ignored){}
}
};
admin.start();
}//ServeurImpots
Oluşturucudaki tek yeni parametre, istemcilerin isteklerini dinlemek için kullanılan bağlantı noktasıdır. Diğer parametreler doğrudan impotsJDBC temel sınıfına aktarılır. Vergi sunucusu, klavyeden girilen komutlarla kontrol edilir. Bu nedenle, bu komutları okumak için bir iş parçacığı oluşturulur. İki olası komut olacaktır: Hizmeti başlatmak için start, hizmeti kalıcı olarak durdurmak için stop. Bu komutları yöneten admin yöntemi şöyledir:
public void admin() throws IOException{
// klavyeden girilen sunucu yönetim komutlarını okur
// sonsuz bir döngü içinde
String commande=null;
BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));
while(true){
// komut istemi görüntüler
System.out.print("Serveur d'impôts>");
// komut okuma
commande=IN.readLine().trim().toLowerCase();
// komutun yürütülmesi
if(commande.equals("start")){
// aktif mi?
if(actif){
//hata
System.out.println("Le serveur est déjà actif");
// devam ediliyor
continue;
}//if
// dinleme hizmeti oluşturuluyor ve başlatılıyor
Thread ecoute=new Thread(){
public void run(){
ecoute();
}
};
ecoute.start();
}//if
else if(commande.equals("stop")){
// tüm yürütme iş parçacıklarının sonu
System.exit(0);
}//eğer
else {
// hata
System.out.println("Commande incorrecte. Utilisez (start,stop)");
}//if
}//while
}//yönetici
Klavyeyle girilen komut start ise, istemci isteklerini dinleyen bir iş parçacığı başlatılır. Girilen komut stop ise, tüm iş parçacıkları durdurulur. Dinleme iş parçacığı, ecoute yöntemini yürütür:
public void ecoute(){
// istemcilerin isteklerini dinleyen iş parçacığı
// dinleme hizmeti oluşturuluyor
ServerSocket ecoute=null;
try{
// hizmet oluşturuluyor
ecoute=new ServerSocket(portEcoute);
// izleme
System.out.println("Serveur d'impôts lancé sur le port " + portEcoute);
// hizmet döngüsü
Socket liaisonClient=null;
while (true){ // sonsuz döngü
// istemci bekleme
liaisonClient=ecoute.accept();
// hizmet başka bir görev tarafından sağlanıyor
new traiteClientImpots(liaisonClient,this).start();
// istekleri dinlemeye geri dönülür
}// while döngüsünün sonu
}catch(Exception ex){
// hata bildiriliyor
erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
}//catch
}//dinleme iş parçacığı
portEcoute numaralı bağlantı noktasında dinleme yapan klasik bir TCP sunucusu bulunmaktadır. İstemcilerin istekleri, traiteCientImpots iş parçacığının run yöntemi tarafından işlenir; bu yöntemin oluşturucusuna iki parametre aktarılır:
- liaisonClient adlı Socket nesnesi, müşteriye erişim sağlayacaktır
- impotsJDBC ve this nesneleri, vergi hesaplama yöntemi olan this.calculer'e erişim sağlayacaktır.
// -------------------------------------------------------
// vergi sunucusunun bir müşterisine hizmet sağlar
class traiteClientImpots extends Thread{
private Socket liaisonClient; // istemciyle bağlantı
private BufferedReader IN; // giriş akışı
private PrintWriter OUT; // çıkış akışı
private impotsJDBC objImpots; // Vergi nesnesi
// oluşturucu
public traiteClientImpots(Socket liaisonClient,impotsJDBC objImpots){
this.liaisonClient=liaisonClient;
this.objImpots=objImpots;
}//üretici
run yöntemi, müşterilerin taleplerini işler. Bunlar, iki farklı biçimde olabilen metin satırlarıdır:
- evli olup olmadığı hesaplaması (evet/hayır) nbEnfants salaireAnnuel
- fincalculs
Biçim 1, vergi hesaplamasına izin verirken, biçim 2 müşteri-sunucu bağlantısını sonlandırır.
// çalıştırma yöntemi
public void run(){
// hizmeti müşteriye sunar
try{
// giriş akışı
IN=new BufferedReader(new InputStreamReader(liaisonClient.getInputStream()));
// çıkış akışı
OUT=new PrintWriter(liaisonClient.getOutputStream(),true);
// müşteriye hoş geldiniz mesajı gönderme
OUT.println("Bienvenue sur le serveur d'impôts");
// istek/yanıt okuma döngüsü
String demande=null;
String[] champs=null; // isteğin bileşenleri
String commande=null; // müşterinin komutu: hesaplama veya hesaplamaların sonu
while ((demande=IN.readLine())!=null){
// isteği alanlara ayrıştırma
champs=demande.trim().toLowerCase().split("\\s+");
// kabul edilen iki istek: hesaplama ve hesaplamaların sonu
commande=champs[0];
if(! commande.equals("calcul") && ! commande.equals("fincalculs")){
// müşteri hatası
OUT.println("Commande incorrecte. Utilisez (calcul,fincalculs).");
// sonraki sipariş
continue;
}//if
if(commande.equals("calcul")) calculerImpôt(champs);
if(commande.equals("fincalculs")){
// müşteriye veda mesajı
OUT.println("Au revoir...");
// kaynakların serbest bırakılması
try{ OUT.close();IN.close();liaisonClient.close();}
catch(Exception ex){}
// son
return;
}//if
//bir sonraki istek
}//while
}catch (Exception e){
erreur("L'erreur suivante s'est produite ("+e+")",2);
}// son try
}// Run sonu
Vergi hesaplaması, müşterinin yaptığı talebin alan tablosunu parametre olarak alan calculerImpôt yöntemi ile gerçekleştirilir. Talebin geçerliliği kontrol edilir ve gerekirse vergi hesaplanarak müşteriye geri gönderilir.
// vergi hesaplaması
public void calculerImpôt(String[] champs){
// başvuruyu işler: evli hesaplaması nbEnfants salaireAnnuel
// alan tablosunda alanlara ayrılmış
String marié=null;
int nbEnfants=0;
int salaireAnnuel=0;
// argümanların geçerliliği
try{
// en az 4 alan gereklidir
if(champs.length!=4) throw new Exception();
// evli
marié=champs[1];
if (! marié.equals("o") && ! marié.equals("n")) throw new Exception();
// çocuklar
nbEnfants=Integer.parseInt(champs[2]);
// maaş
salaireAnnuel=Integer.parseInt(champs[3]);
}catch (Exception ignored){
// biçim hatası
OUT.println(" syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel");
// bitti
return;
}//eğer
// vergi hesaplanabilir
long impot=objImpots.calculer(marié.equals("o"),nbEnfants,salaireAnnuel);
// cevap müşteriye gönderilir
OUT.println(""+impot);
}//hesapla
Bir test programı şu şekilde olabilir:
// çağrı: serveurImpots bağlantı noktası dsnImpots userImpots mdpImpots
import java.io.*;
public class testServeurImpots{
public static final String syntaxe="Syntaxe : pg port dsnImpots userImpots mdpImpots";
// ana program
public static void main (String[] args){
// 4 argüman gereklidir
if(args.length != 4)
erreur(syntaxe,1);
// bağlantı noktası >0 olan bir tamsayı olmalıdır
int port=0;
boolean erreurPort=false;
Exception E=null;
try{
port=Integer.parseInt(args[0]);
}catch(Exception e){
E=e;
erreurPort=true;
}
erreurPort=erreurPort || port <=0;
if(erreurPort)
erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);
// vergi sunucusu oluşturuluyor
try{
new ServeurImpots(port,args[1],args[2],args[3]);
}catch(Exception ex){
//hata
System.out.println("L'erreur suivante s'est produite : "+ex.getMessage());
}//catch
}//Ana
// hataların görüntülenmesi
public static void erreur(String msg, int exitCode){
// hata görüntüleme
System.err.println(msg);
// hata ile durdurma
System.exit(exitCode);
}//hata
}// sınıf sonu
ServeurImpots nesnesinin oluşturulması için gerekli verileri test programına aktarıyoruz ve program bu verileri kullanarak söz konusu nesneyi oluşturuyor.
İlk çalıştırmayı deneyelim:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
stop
Komut
komutu, henüz istemcilerin isteklerini dinlemeyen bir ServeurImpots nesnesi oluşturur. Bu dinleme işlemini başlatan, klavyeden girilen start komutudur. stop komutu ise sunucuyu durdurur. Şimdi bir istemci kullanalım. Daha önce oluşturduğumuz genel istemciyi kullanacağız. Sunucu başlatıldı:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
Genel istemci başka bir DOS penceresinde başlatılır:
İstemcinin sunucudan karşılama mesajını başarıyla aldığını görüyoruz. Başka komutlar gönderiyoruz:
x
<-- Commande incorrecte. Utilisez (calcul,fincalculs).
calcul
<-- syntaxe : calcul marié(O/N) nbEnfants salaireAnnuel
calcul o 2 200000
<-- 22506
calcul n 2 200000
<-- 33388
fincalculs
<-- Au revoir...
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]
Sunucuyu durdurmak için sunucu penceresine geri dönüyoruz:
dos>java testServeurImpots 124 mysql-dbimpots admimpots mdpimpots
Serveur d'impôts>start
Serveur d'impôts>Serveur d'écho lancé sur le port 124
stop
8.5. Alıştırmalar
8.5.1. Alıştırma 1 - TCP genel grafik istemcisi
8.5.1.1. Uygulamanın Tanıtımı
İnternet üzerinden başlıca TCP hizmetleriyle iletişim kurabilen bir program oluşturmayı hedefliyoruz. Buna genel amaçlı bir TCP istemcisi adını vereceğiz. Bu uygulamayı anladıktan sonra, tüm TCP istemcilerinin birbirine benzediğini görürüz. Programın penceresi şu şekildedir:

Çeşitli denetimlerin anlamları şöyledir:
No | ad | tür | işlev |
1 | TxtRemoteHost | JTextField | istenen hizmeti sunan makinenin adı |
2 | TxtPort | JTextField | İstenen hizmetin bağlantı noktası |
3 | TxtSend | JTextField | istemci tarafından sunucuya gönderilecek mesaj metni |
4 | OptRCLF OptLF | JCheckBox | istemci/sunucu iletişiminde satırların nasıl sonlandırılacağını belirten düğmeler RCLF: satır başı (#13) + satır sonu (#10) LF: satır sonu (#10) |
5 | LstSuivi | JList | istemci ile sunucu arasındaki iletişim durumuna ilişkin mesajları görüntüler |
6 | LstDialogue | JList | istemci (->) ve sunucu (<-) arasında alışverişi yapılan mesajları gösterir |
7 | CmdAnnuler | JButton | gizli - diyalog listesinin altında bulunur - Bağlantı kurulurken görünür ve sunucu yanıt vermezse bağlantıyı kesmeye olanak tanır |
Kullanılabilir menü seçenekleri şunlardır:
seçenek | alt seçenekler | rol |
Bağlan | Bağlan | istemciyi sunucuya bağlar |
Bağlantıyı kes | bağlantıyı sonlandırır | |
Çık | Programı sonlandırır | |
Mesajlar | Gönder | TxtSend denetim mesajını sunucuya gönderir |
RazSuivi | LstSuivi listesini sil | |
RazDialogue | LstDialogue listesini sil | |
Yazar | telif hakkı kutucuğunu görüntüler |
8.5.1.2. FONCTIONNEMENT DE APPLICATION
Uygulamanın ana sayfası yüklendiğinde, aşağıdaki işlemler gerçekleştirilir:
- sayfa ekranın ortasına yerleştirilir
- yalnızca Connexion/Quitter ve Auteur menü seçenekleri etkindir
- Annuler düğmesi gizlenir
- LstSuivi ve LstDialogue listeleri boştur
Bu seçenek, yalnızca Uzak Ana Bilgisayar ve Bağlantı Noktası No. alanları boş olmadığında ve şu anda aktif bir bağlantı bulunmadığında kullanılabilir. Bu seçeneğe tıklandığında aşağıdaki işlemler gerçekleştirilir:
- bağlantı noktasının geçerliliği kontrol edilir: bu, 0'dan büyük bir tamsayı olmalıdır
- sunucuya bağlantıyı sağlamak için bir iş parçacığı başlatılır
- kullanıcının devam eden bağlantıyı kesebilmesi için Annuler düğmesi görünür
- Quitter ve Auteur hariç tüm menü seçenekleri devre dışı bırakılır
Bağlantı çeşitli şekillerde sonlandırılabilir:
- Kullanıcı Annuler düğmesine bastı: bağlantı iş parçacığı durdurulur ve menü başlangıç durumuna geri döndürülür. Takip bölümünde, bağlantının kullanıcı tarafından kapatıldığı belirtilir.
- Bağlantı bir hata ile sonlanır: yukarıdaki işlemlerin aynısı yapılır ve ayrıca izleme bilgisinde hatanın nedeni belirtilir.
- Bağlantı başarıyla sona erer: Annuler düğmesini kaldırırız, izleme bölümünde bağlantının kurulduğunu belirtiriz, RazSuivi menüsünü etkinleştiririz, Connecter menüsünü devre dışı bırakırız, Déconnecter menüsünü etkinleştiririz
Bu seçenek yalnızca sunucuyla bir bağlantı mevcut olduğunda kullanılabilir. Etkinleştirildiğinde sunucuyla olan bağlantıyı sonlandırır ve menüyü başlangıç durumuna geri döndürür. Takip bölümünde bağlantının istemci tarafından kapatıldığı belirtilir.
Bu seçenek, sunucuyla varsa mevcut bağlantıyı sonlandırır ve uygulamayı kapatır.
Bu seçeneğe yalnızca aşağıdaki koşullar sağlandığında erişilebilir:
-
sunucuya bağlantı kurulmuşsa
-
gönderilecek bir mesaj var
Bu koşullar sağlanırsa, RCLF seçeneği işaretlenmişse, RCLF dizisiyle biten TxtSend (3) alanındaki metin sunucuya gönderilir; aksi takdirde LF dizisiyle biten metin gönderilir. Gönderim sırasında meydana gelebilecek bir hata, takip listesinde bildirilir.
Sırasıyla LstSuivi ve LstDialogue listelerini temizler. İlgili listeler boş olduğunda bu seçenekler devre dışı kalır.
Formun alt kısmında bulunan bu düğme, yalnızca müşteri sunucuya bağlanmaya çalışırken görünür. Sunucu yanıt vermediği veya hatalı yanıt verdiği için bu bağlantı kurulamayabilir. Bu durumda Annuler düğmesi, kullanıcıya bağlantı isteğini sonlandırma olanağı sunar.
LstSuivi (5) listesi, bağlantıyı takip eder. Bu liste, bağlantının önemli anlarını gösterir:
-
istemci tarafından açılması
-
sunucu veya müşteri tarafından kapatılması
-
bağlantı aktif olduğu süre boyunca meydana gelebilecek tüm hatalar
LstDialogue (6) listesi, istemci ile sunucu arasında kurulan diyaloğu izler. Bir iş parçacığı, arka planda istemcinin iletişim soketinde neler olup bittiğini izler ve bunları 6 numaralı listede görüntüler.
Bu menü, Telif Hakkı adı verilen bir pencere açar:

Bağlantı hataları 6 izleme listesinde, istemci/sunucu iletişimi ile ilgili hatalar ise 7 iletişim listesinde bildirilir. Bir bağlantı hatası meydana geldiğinde, istemci/sunucu iletişimi sonlandırılır ve form, yeni bir bağlantıya hazır olacak şekilde başlangıç durumuna geri döndürülür.
8.5.1.3. TRAVAIL A FAIRE
Yukarıda açıklanan işi iki şekilde gerçekleştirin:
- bağımsız uygulama
- applet
8.5.2. Alıştırma 2 - Bir kaynak sunucusu
8.5.2.1. INTRODUCTION
Bir kurumun, İnternet üzerinden erişilebilen birkaç güçlü hesaplama sunucusu bulunmaktadır. Bu hesaplama hizmetlerini kullanmak isteyen her makine, sunuculardan birinin 756 numaralı bağlantı noktasına bir veri dosyası gönderir. Bu dosya çeşitli bilgileri içerir: kullanıcı adı, şifre, istenen hesaplama türünü belirten komutlar ve hesaplamanın yapılacağı veriler. Veri dosyası doğruysa, seçilen hesaplama sunucusu bu dosyayı kullanır ve sonuçları bir metin dosyası biçiminde istemciye geri gönderir.
Böyle bir düzenlemenin birçok avantajı vardır:
- Her tür istemci (PC, Mac, Unix,...) bu hizmeti kullanabilir
- istemci İnternet'in herhangi bir yerinde olabilir
- hesaplama kaynakları optimize edilmiştir: yalnızca birkaç güçlü makine gereklidir. Böylece, hesaplama kaynaklarına sahip olmayan küçük bir kuruluş bile, kullanılan hesaplama süresine göre hesaplanan bir mali katkı karşılığında bu hizmeti kullanabilir.
Makinelerin gücü ne kadar yüksek olursa olsun, bir hesaplama bazen birkaç saat sürebilir: bu durumda sunucu diğer müşteriler için kullanılamaz hale gelir. Bu durumda, müşteri için kullanılabilir bir hesaplama sunucusu bulma sorunu ortaya çıkar. Bunun için, bundan sonra GRC sunucusu olarak adlandırılacak bir “hesaplama kaynakları yöneticisi” kullanılır. Bu hizmet tek bir makineye yerleştirilmiştir ve TCP modunda 864 numaralı bağlantı noktasında çalışır. Hesaplama sunucusuna erişim isteyen bir müşteri, bu hizmete başvurur. Hesaplama sunucularının tam listesine sahip olan GRC sunucusu, müşteriye şu anda aktif olmayan bir sunucunun adını göndererek yanıt verir. Böylece istemcinin, kendisine belirtilen sunucuya verilerini göndermesi yeterlidir.
GRC sunucusunu yazmayı planlıyoruz.
8.5.2.2. INTERFACE VISUELLE
Görsel arayüz şu şekilde olacaktır:

Arayüzde iki sunucu listesi bulunur:
- sol tarafta, hesaplamalar için kullanılabilir olan aktif olmayan sunucuların listesi
- sağda ise bir müşterinin hesaplamalarıyla meşgul olan sunucuların listesi yer almaktadır.
Menü yapısı şu şekildedir:
Ana Menü | İkincil Menü | Rol |
Hizmet | Başlat | 864 numaralı bağlantı noktasında tcp hizmetini başlat |
Durdur | Hizmeti durdurur | |
Çık | Uygulamayı sonlandır | |
Yazar | Telif Hakkı Bilgileri |
Formda bulunan denetimlerin yapısı şu şekildedir:
Ad | Tür | Rol |
listLibres | JList | Boş sunucu listesi |
listOccupés | JList | Meşgul sunucuların listesi |
8.5.2.3. FONCTIONNEMENT DE APPLICATION
Uygulama yüklendiğinde, listLibres listesi, GRC tarafından yönetilen hesaplama sunucularının adlarının listesiyle doldurulur. Bu sunucular, parametre olarak geçirilen bir Sunucular dosyasında tanımlanmıştır. Bu dosya, her satırda bir sunucu adı olacak şekilde bir sunucu adları listesi içerir ve bu nedenle listLibres listesini doldurmak için kullanılır. Başlat menüsü etkinleştirilir, Durdur menüsü devre dışı bırakılır.
Bu seçenek
- makinenin 864 numaralı bağlantı noktasını dinleyen hizmeti başlatır
- "Başlat" menüsünü devre dışı bırakır
- "Durdur" menüsünü etkinleştirir
Bu seçenek hizmeti durdurur:
- meşgul sunucular listesi boşaltılır
- boş sunucu listesi, Sunucular dosyasının içeriğiyle doldurulur
- "Başlat" menüsü etkinleştirilir
- "Sonlandır" menüsü devre dışı bırakılır
Uygulama sonlandırılır.
İstemci/sunucu iletişimi, RCLF dizisiyle biten metin satırlarının alışverişi yoluyla gerçekleşir. GRC sunucusu iki komutu tanır: getserveur ve finservice. Bu iki komutun işlevlerini ayrıntılı olarak açıklıyoruz:
- 1-getserveur
İstemci, kendisi için kullanılabilir bir hesaplama sunucusu olup olmadığını sorar.
GRC sunucusu, boş sunucu listesinde bulunan ilk sunucuyu seçer ve adını şu biçimde istemciye geri gönderir:
Ayrıca, müşteriye tahsis edilen sunucuyu şu biçimde meşgul sunucular listesine ekler:
Aşağıdaki örnekte de görüldüğü gibi, calcul1.istia.univ-angers.fr sunucusu, IP ve 193.52.43.5 adresli istemcilere hizmet vermekle meşgul:

Bir istemciye halihazırda bir hesaplama sunucusu atanmışsa, istemci getserveur komutunu gönderemez. Bu nedenle, istemciye yanıt vermeden önce GRC sunucusu, istemcinin IP adresinin meşgul sunucular listesinde kayıtlı adresler arasında bulunmadığını kontrol eder. Eğer bulunuyorsa, GRC sunucusu şu yanıtı verir:
Son olarak, hiçbir hesaplama sunucusunun müsait olmadığı durum da söz konusudur: boş sunucu listesi boştur. Bu durumda, GRC sunucusu şu yanıtı verir:
Her durumda, istemciye yanıt verdikten sonra, GRC sunucusu diğer istemcilere hizmet verebilmek için istemciyle olan bağlantıyı sonlandırır.
- 2-finservice
İstemci, kullandığı hesaplama sunucusuna artık ihtiyaç duymadığını belirtir.
GRC sunucusu, öncelikle bu istemcinin gerçekten kendisine ait bir istemci olup olmadığını kontrol eder. Bunun için, müşterinin IP adresinin, meşgul sunucular listesinde kayıtlı adresler arasında olup olmadığını kontrol eder. Eğer değilse, GRC sunucusu şu yanıtı verir:
İstemci tanınırsa, GRC sunucusu şu yanıtı verir:
ve bu müşteriye atanan hesaplama sunucusunu boş sunucular listesine aktarır. Önceki örneği ele alırsak, müşteri finservice komutunu gönderirse, GRC sunucusunun ekranı şu şekilde olur:

Yanıt gönderildikten sonra, yanıtın içeriği ne olursa olsun, GRC sunucusu bağlantıyı sonlandırır.
8.5.2.4. TRAVAIL A FAIRE
Uygulamayı, örneğin telnet istemcisiyle veya önceki alıştırmadaki genel tcp istemcisiyle test edilebilecek bağımsız bir program olarak yazın.
8.5.3. Alıştırma 3 - bir SMTP istemcisi
8.5.3.1. INTRODUCTION
Burada, e-posta göndermeye olanak tanıyan SMTP (SendMail Aktarım Protokolü) hizmeti için bir istemci oluşturmak istiyoruz. Unix veya Windows altında, telnet programı, tcp protokolüyle çalışan bir istemcidir. Bu program, tcp dizisiyle biten metin biçimindeki komutları kabul eden tüm RCLF hizmetleriyle “iletişim kurabilir”; yani ASCII kodlu 13 ve 10 karakterlerini kabul eden hizmetlerle. Aşağıda, e-posta gönderme hizmeti smtp ile yapılan bir iletişim örneği yer almaktadır:
// SMTP sunucusundan gelen yanıt
Trying 193.52.43.2...
Connected to istia.univ-angers.fr.
Escape character is '^]'.
220-Istia.Istia.Univ-Angers.fr Sendmail 8.6.10/8.6.9 ready at Tue, 16 Jan 1996 07:53:12 +0100
220 ESMTP spoken here
// yorumlar --------------
telnet programı, şu sözdizimiyle herhangi bir hizmeti çağırabilir
telnet machine_service port_service
İstemci/sunucu iletişimi, RCLF dizisiyle biten metin satırlarıyla gerçekleştirilir.
smtp hizmetinin yanıtları şu biçimdedir:
Mesaj numarası veya
Mesaj numarası
SMTP sunucusu birden fazla yanıt satırı gönderebilir. Yanıtın son satırı, bir numara ve ardından bir boşluk ile belirtilirken, yanıtın önceki satırlarında numaranın ardından bir tire (-) işareti gelir.
500'e eşit veya 500'den büyük bir numara, bir hata mesajını belirtir.
// yorumların sonu
// SMTP sunucusundan gelen yanıt
214-Commands:
214- HELO EHLO MAIL RCPT DATA
214- RSET NOOP QUIT HELP VRFY
214- EXPN VERB
214-For more info use "HELP <topic>".
214-To report bugs in the implementation send email to
214- sendmail@CS.Berkeley.EDU.
214-For local information send email to Postmaster at your site.
214 End of HELP info
// yorumlar ---------
mail komutunun sözdizimi şöyledir:
mail from: mesajın göndereninin e-posta adresi
// yorumların sonu
// SMTP sunucusundan gelen yanıt
// yorumlar
smtp sunucusu, gönderenin adresinin geçerliliğini kontrol etmez: adresi kendisine verildiği şekliyle kabul eder
// yorumların sonu
// yorumlar ---------
rcpt komutunun sözdizimi şöyledir:
rcpt to: mesajın alıcısının e-posta adresi
E-posta adresi, smtp sunucusunun çalıştığı makinedeki bir adres ise, sunucu adresin varlığını kontrol eder; aksi takdirde herhangi bir kontrol yapmaz. Kontrol yapılmış ve bir hata tespit edilmişse, bu hata >= 500 numaralı bir kodla bildirilir.
İstenilen sayıda rcpt to komutu verilebilir: bu, bir mesajı birden fazla kişiye göndermeyi sağlar.
// yorumların sonu
// SMTP sunucusundan gelen yanıt
// yorumlar ---------
data komutunun sözdizimi şöyledir:
data
satır1
satır2
...
.
Bunu, mesajı oluşturan metin satırları izler; mesaj, yalnızca "nokta" karakterini içeren bir satırla sona ermelidir.
Mesaj daha sonra rcpt komutuyla belirtilen alıcıya gönderilir.
// yorum sonu
// SMTP sunucusundan gelen yanıt
// klavyeden girilen mesaj metni
subject: essai smtp
essai smtp a partir de telnet
.
// yorumlar
data komutunun metin satırlarına, e-postanın konusunu belirtmek için bir subject: satırı eklenebilir. Bu satırın ardından boş bir satır gelmelidir.
// SMTP sunucusundan gelen yanıt
// yorumlar
quit komutu, smtp hizmetiyle olan bağlantıyı sonlandırır
// yorumların sonu
// SMTP sunucusundan gelen yanıt
8.5.3.2. INTERFACE VISUELLE
Aşağıdaki görsel arayüze sahip bir program yazmayı planlıyoruz:

Kontroller şu işlevlere sahiptir:
Numara | Tür | İşlev |
1 | JTextField | Virgülle ayrılmış e-posta adresleri dizisi |
2 | JTextField | Mesajın konu metni |
3 | JTextField | Virgülle ayrılmış e-posta adresleri dizisi |
4 | JTextField | Virgülle ayrılmış e-posta adresleri dizisi |
5 | JTextArea | Mesaj metni |
6 | JList | izleme listesi |
7 | JList | diyalog listesi |
8 | JButton | "İptal" düğmesi gösterilmiyor; bu düğme, istemci SMTP sunucusuna bağlantı talep ettiğinde görünür. Sunucu yanıt vermezse, kullanıcının bu talebi iptal etmesini sağlar. |
8.5.3.3. LES MENUS
Uygulamanın menü yapısı şu şekildedir:
Ana Menü | İkincil Menü | Rol |
Posta | ||
Gönder | Kontrol 5'in mesajını gönder | |
Çık | Uygulamadan çık | |
Seçenekler | ||
İzlemeyi Gizle | Kontrol 6'yı görünmez hale getir | |
İzlemeyi Sıfırla | Takip listesini boşalt 6 | |
Diyalogu Gizle | Diyalog listesini gizler 7 | |
Diyalog Sil | Diyalog listesini boşalt 7 | |
Yapılandır | Kullanıcının aşağıdakileri belirlemesine olanak tanır - program tarafından kullanılan SMTP sunucusunun adresini - e-posta adresini | |
Kaydet... | Önceki yapılandırmayı bir .ini dosyasına kaydeder | |
Yazar | Telif Hakkı Bilgileri |
8.5.3.4. FONCTIONNEMENT DE APPLICATION
Bu menü, aşağıdaki pencerenin açılmasını sağlar:

OK düğmesinin etkin hale gelmesi için her iki alanın da doldurulması gerekir. Bu iki bilginin diğer modüller tarafından kullanılabilmesi için küresel değişkenlere kaydedilmesi gerekir.
Bu seçeneğe yalnızca aşağıdaki koşullar sağlandığında erişilebilir:
- yapılandırma tamamlanmışsa
- gönderilecek bir mesaj var
- bir konu başlığı varsa
- 1, 3 ve 4 numaralı alanlarda en az bir alıcı var
Bu koşullar sağlanırsa, olayların sırası şu şekildedir:
- form, istemci/sunucu iletişimini engelleyebilecek tüm eylemlerin devre dışı bırakıldığı bir duruma getirilir
- yapılandırmada belirtilen sunucunun 25 numaralı bağlantı noktasında bağlantı kurulur
- ardından istemci, yukarıda açıklanan protokole göre SMTP sunucusuyla iletişim kurar
- "From:" satırında, yapılandırmada belirtilen gönderenin e-posta adresi kullanılır
- "rcpt to:" alanı, 1, 3 ve 4 numaralı alanlarda bulunan her bir e-posta adresi için kullanılır
- data komutundan sonra gönderilen satırlarda şu metinler bulunur:
- bir Subject: satırı, kontrol 2'nin konu metni
- bir satır Cc: denetimin adresleri 3
- bir satır Bcc: denetim adresleri 4
- kontrol 5'in mesaj metni
- son nokta
Formun altında bulunan bu düğme, yalnızca müşteri smtp sunucusuna bağlanırken görünür. smtp sunucusu yanıt vermediği veya hatalı yanıt verdiği için bu bağlantı kurulamayabilir. Bu durumda Annuler düğmesi, kullanıcıya bağlantı isteğini sonlandırma olanağı sunar.
(6) listesi, bağlantıyı takip eder. Bu liste, bağlantının önemli anlarını gösterir:
- istemci tarafından açılması
- sunucu veya müşteri tarafından kapatılması
- tüm bağlantı hataları
(7) listesi, istemci ile sunucu arasında kurulan smtp diyaloğunu izler.
Bu iki liste, menüdeki şu seçeneklerle ilişkilidir:
Takip Listesi'ni Gizle | 6 numaralı takip listesini ve üstündeki etiketi görünmez hale getirir. Bu iki kontrolün kapladığı yükseklik H ise, altındaki tüm kontroller H yükseklik kadar yukarı kaydırılır ve formun toplam boyutu H kadar azalır. Ayrıca, “Takibi Gizle” seçeneği, aşağıdaki RazSuivi seçeneğini görünmez hale getirir. |
İzlemeyi Sıfırla | Takip listesini boşaltır 6 |
Diyalog Penceresi Gizle | 7 diyalog listesini, üzerindeki etiketi ve aşağıdaki RazDialogue menü seçeneğini görünmez hale getirir. "Takibi Gizle" seçeneğinde olduğu gibi, altındaki denetimlerin konumu (örneğin Annuler düğmesi) yeniden hesaplanır ve pencere boyutu küçültülür. |
Raz Diyalog | 7 diyalog listesini boşaltır |
Bu menü, Telif Hakkı adı verilen bir pencere açar:

Bağlantı hataları 6 izleme listesinde, istemci/sunucu iletişimiyle ilgili hatalar ise 7 iletişim listesinde bildirilir. Bir hata oluştuğunda, kullanıcı bir hata kutusu ile uyarılır ve hatanın nedeni, daha önce gizlenmişse, bu nedeni içeren liste görüntülenir. Ayrıca, istemci/sunucu diyaloğu kapatılır ve form başlangıç durumuna geri döndürülür.
8.5.3.5. GESTION, UN, FICHIER, DE, CONFIGURATION
Kullanıcının yazılımı her kullandığında yeniden yapılandırmak zorunda kalmaması tercih edilir. Bunun için, Seçenekler/Çıkarken yapılandırmayı kaydet seçeneği işaretliyse, program kapatıldığında Options/Configurer seçeneği ile elde edilen iki bilgi ile iki takip listesinin durumu, programın .exe dosyasıyla aynı dizinde bulunan sendmail.ini adlı bir dosyaya kaydedilir. Bu dosya şu biçimdedir:
SmtpServer=shiva.istia.univ-angers.fr
ReplyAddress=serge.tahe@istia.univ-angers.fr
Suivi=0
Dialogue=1
SmtpServer ve ReplyAddress satırları, Seçenekler/Yapılandır seçeneği ile alınan iki bilgiyi içerir. Suivi ve Dialogue satırları, Takip ve Diyalog listelerinin durumunu gösterir: 1 (mevcut), 0 (yok).
Program yüklendiğinde, sendmail.ini dosyası varsa okunur ve form buna göre yapılandırılır. sendmail.ini dosyası yoksa, sanki bu dosya varmış gibi işlem yapılır:
sendmail.ini dosyası mevcutsa ancak eksikse (satır eksik), eksik satır yukarıdaki karşılık gelen satırla değiştirilir. Dolayısıyla, Suivi=... satırı eksikse, sanki Suivi=1 dosyası varmış gibi işlem yapılır.
Şablona uymayan tüm satırlar:
şablonuna uymayan tüm satırlar ve anahtar kelimenin geçersiz olduğu satırlar göz ardı edilir. Anahtar kelime büyük veya küçük harflerle yazılabilir: bu fark etmez.
Options/Configurer seçeneğinde, şu anda geçerli olan SmtpServer ve ReplyAddress değerleri gösterilir. Kullanıcı isterse bu değerleri değiştirebilir.
8.5.3.6. TRAVAIL → FAIRE
Yukarıda açıklanan işlemi gerçekleştirin. Yapılandırma dosyasının yönetimini en son halletmeniz tavsiye edilir.
8.5.4. Alıştırma 4 - POPPASS istemcisi
8.5.4.1. Introduction
106 numaralı bağlantı noktasında çalışan POPPASSD sunucusuyla iletişim kurabilen bir TCP istemcisi oluşturulacaktır. Bu hizmet, UNIX makinesinde şifre değiştirilmesine olanak tanır. İstemci/Sunucu iletişim protokolü şu şekildedir:
1 - İletişim, RCLF dizisiyle biten mesajların alışverişi yoluyla gerçekleşir
2 - İstemci, sunucuya komutlar gönderir
- Sunucu, 3 basamaklı sayılarla başlayan mesajlarla yanıt verir: XXX. Eğer XXX=200 ise, komut başarıyla yürütülmüştür; aksi takdirde bir hata oluşmuştur.
3 - İletişim akışı şu şekildedir:
- sunucu bir hoş geldiniz mesajıyla yanıt verir
- Sunucu, giriş kabul edilirse şifre sorarak yanıt verir; aksi takdirde bir hata mesajı gönderir
- Şifre kabul edilirse sunucu yeni şifre sorarak yanıt verir, aksi takdirde bir hata mesajı gönderir
- sunucu, yeni şifrenin kabul edildiğini onaylayarak yanıt verir; aksi takdirde bir hata mesajı gönderir
- sunucu bir sonlandırma mesajı gönderir ve bağlantıyı kapatır
8.5.4.2. İstemci formu

Çeşitli denetimlerin anlamları şöyledir:
No | ad | tür | işlev |
1 | txtRemoteHost | JTextField | sunucu adı |
2 | txtLogin | JTextField | kullanıcı adı |
3 | txtMdp | JTextField | Kullanıcı şifresi |
4 | txtNewMdp | JTextField | Kullanıcının yeni şifresi |
5 | txtConfirmation | JTextField | Yeni şifrenin onaylanması |
6 | lstSuivi | JList | Oturum açma izleme mesajları |
7 | lstDialogue | JList | İstemci/Sunucu diyalog mesajları |
10 | cmdAnnuler | JButton | gösterilmiyor - Sunucuya bağlantı kurulurken görünen düğme. Bağlantıyı sonlandırmaya yarar. |
8.5.4.3. Menüler
Başlık | Kontrolün adı | İşlev |
Bağlantı | mnuconnexion | |
Bağlan | mnuconnecter | sunucuya bağlantı başlat |
Çık | mnuQuitter | uygulamadan çıkar |
Mesajlar | mnuMessages | |
RazSuivi | mnuRazSuivi | listeyi sil lstSuivi |
RazDialogue | mnuRazDialogue | listeyi sil lstDialogue |
Yazar | mnuAuteur | telif hakkı kutucuğunu gösterir |
8.5.4.4. Uygulamanın Çalışması
Uygulamanın ana sayfası yüklendiğinde, aşağıdaki işlemler gerçekleştirilir:
- sayfa ekranın ortasına yerleştirilir
- yalnızca Connexion/Quitter ve Auteur menü seçenekleri etkindir
- Annuler düğmesi gizlenir
- LstSuivi ve LstDialogue listeleri boştur
Bu seçenek yalnızca 1 ila 5 numaralı alanlar doldurulduğunda kullanılabilir. Bu seçeneğe tıklandığında aşağıdaki işlemler gerçekleştirilir:
- sunucuya bağlantı sağlamak için bir iş parçacığı başlatılır
- kullanıcının devam eden bağlantıyı kesebilmesi için Annuler düğmesi görünür
- Quitter ve Auteur hariç tüm menü seçenekleri devre dışı bırakılır
Ardından olaylar şu sırayla gerçekleşir:
- Kullanıcı Annuler düğmesine bastı: bağlantı iş parçacığı durdurulur ve menü başlangıç durumuna geri getirilir. İzleme bölümünde, bağlantının kullanıcı tarafından kapatıldığı belirtilir.
- Bağlantı isteği sunucu tarafından kabul edilir. Ardından, şifreyi değiştirmek için sunucuyla iletişim başlatılır. Bu iletişimin verileri LstDialogue listesine kaydedilir. İletişim sona erdiğinde, sunucuyla olan bağlantı kapatılır ve form menüsü başlangıç durumuna geri döndürülür.
- İletişim devam ettiği sürece, kullanıcının isterse bağlantıyı sonlandırabilmesi için İptal düğmesi ekranda kalır.
- İletişim sırasında herhangi bir hata meydana gelirse, bağlantı sonlandırılır ve hata nedeni LstSuivi izleme listesinde görüntülenir.
Bu seçenek, sunucuyla aktif olan bağlantıyı sonlandırır ve uygulamayı kapatır.
Sırasıyla LstSuivi ve LstDialogue listelerini boşaltır. İlgili listeler boş olduğunda bu seçenekler devre dışı kalır.
Formun alt kısmında bulunan bu düğme, yalnızca müşteri sunucuya bağlanma aşamasındayken veya sunucuya bağlıyken görünür. Annuler düğmesi, kullanıcıya sunucuyla olan iletişimi kesme olanağı sağlar.
LstSuivi (5) listesi, bağlantıyı takip eder. Bağlantının önemli anlarını gösterir:
-
istemci tarafından açılması
-
sunucu veya istemci tarafından kapatılması
-
bağlantı aktif olduğu sürece meydana gelebilecek tüm hatalar
LstDialogue (6) listesi, istemci ile sunucu arasında kurulan diyaloğu izler.
Bu menü, "Telif Hakkı" adı verilen bir pencere açar:

İletişim hataları 6 izleme listesinde, istemci/sunucu iletişimi ile ilgili hatalar ise 7 iletişim listesinde bildirilir. Bağlantı hatası durumunda, istemci/sunucu iletişimi kapatılır ve form, yeni bir bağlantıya hazır olacak şekilde başlangıç durumuna geri döndürülür.
8.5.4.5. TRAVAIL A FAIRE
Yukarıda açıklanan işi önce bağımsız bir uygulama, ardından da bir applet olarak gerçekleştirin.