9. Programların kaynak kodu
9.1. Genel 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 alışverişi 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ı ekrana görüntülemekle görevli bir iş parçacığı
Neden iki iş parçacığı? Her TCP-IP hizmeti kendine özgü bir protokole sahiptir ve bazen aşağıdaki durumlarla karşılaşılabilir:
- istemci, yanıt alabilmek için birkaç satır metin göndermek zorundadır
- bir sunucunun yanıtı birkaç satır metin içerebilir
Bu nedenle, sunucuya tek bir satır gönderme - sunucudan gelen tek bir satırı alma döngüsü her zaman uygun olmayabilir. Bu nedenle, 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ılabilecek 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 çalışma örneğini inceleyelim. Bu hizmet, e-postaları alıcılarına iletmekten 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 adlı gönderen 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 makinedeki kullanıcı adımızı gireriz
- pass password, burada önceki kullanıcı adıyla ilişkili ş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 sunucusuyla 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) 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
9.2. Genel TCP sunucusu
Şimdi,
- bu sunucu, istemcilerinden gelen komutları ekrana yansıtır
- ve yanıt olarak 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 http://localhost:88/exemple.html adresini girelim. Tarayıcı, 88 makinesinin localhost 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
// istemci isteklerini okumak için bir iş parçacığı oluşturur
// bunlar ekranda görüntülenecektir
// klavyeden girilen komutları okumak için bir iş parçacığı oluşturur
// bu komutlar istemciye 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ğrulama
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