Skip to content

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:

  1. klavyeden girilen komutları okumak ve sunucuya göndermekle görevli bir iş parçacığı
  2. 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:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • 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):

Image

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:

commande1
commande2
...
commanden
[ligne vide]

Web sunucusu ancak boş satırı aldıktan sonra yanıt verir. Örnekte sadece bir komut kullandık:

GET /index.html HTTP/1.0

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:

<-- HTTP/1.1 200 OK

, 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:

<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html

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:

Image

Ş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:

Image

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

Image

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  parçacığı oluşturur
     // bunlar ekranda görüntülenecektir
     // klavyeden girilen komutları okumak için bir  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 
     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ı  parçacıklarıyla sağlanır
                nbClients++;

                 // okuma/yazma  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

     //  parçacığının Run yöntemi
    public void run(){

        // yerel veriler
        PrintWriter OUT=null;        //  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{
             //  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){}
         //  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

     //  parçacığının Run yöntemi
    public void run(){

        // yerel veriler
        BufferedReader IN=null;        //  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{
             //  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){
                 //  akışını okuma
                réponse=IN.readLine();
                 // akış kapalı ?
                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){}
         //  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