Skip to content

9. کد منبع برنامه

9.1. کلاینت عمومی TCP

بسیاری از سرویس‌هایی که در روزهای اولیه اینترنت ایجاد شده‌اند، بر اساس مدل سرور اکو که قبلاً مطالعه شد، عمل می‌کنند: ارتباط کلاینت-سرور از طریق تبادل خطوط متن انجام می‌شود. ما قصد داریم یک کلاینت عمومی TCP بنویسیم که به شکل زیر اجرا خواهد شد: java cltTCPgenerique server port

این کلاینت TCP به پورت port روی سرور serveur متصل خواهد شد. پس از انجام این کار، دو تِرد ایجاد خواهد کرد:

  1. یک تِرد مسئول خواندن دستورات تایپ‌شده روی صفحه‌کلید و ارسال آن‌ها به سرور
  2. یک تِرد مسئول خواندن پاسخ‌های سرور و نمایش آن‌ها روی صفحه

چرا دو نخ؟ هر سرویس TCP-IP پروتکل خاص خود را دارد و گاهی اوقات ممکن است شرایط زیر پیش بیاید:

  • کلاینت باید قبل از دریافت پاسخ، چندین خط متن ارسال کند
  • پاسخ یک سرور ممکن است شامل چندین خط متن باشد

بنابراین، حلقه مربوط به ارسال یک خط به سرور و دریافت یک خط از سرور همیشه مناسب نیست. بنابراین ما دو حلقه مجزا ایجاد خواهیم کرد:

  • یک حلقه برای خواندن دستورات تایپ‌شده روی صفحه‌کلید جهت ارسال به سرور. کاربر پایان دستورات را با کلمه کلیدی fin اعلام خواهد کرد.
  • یک حلقه برای دریافت و نمایش پاسخ‌های سرور. این یک حلقه بی‌نهایت خواهد بود که تنها زمانی متوقف می‌شود که سرور اتصال شبکه را قطع کند یا کاربر فرمان fin را تایپ کند.

برای جدا نگه داشتن این دو حلقه، به دو نخ مستقل نیاز داریم. بیایید نگاهی به یک مثال از اجرا بیندازیم که در آن کلاینت عمومی TCP ما به سرویسی به نام SMTP (پروتکل انتقال SendMail) متصل می‌شود. این سرویس مسئول مسیریابی ایمیل‌ها به گیرندگان آن‌ها است. این سرویس روی پورت ۲۵ کار می‌کند و از پروتکل تبادل مبتنی بر متن استفاده می‌کند.


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]

بیایید این مبادلات کلاینت-سرور را تحلیل کنیم:

  • سرویس SMTP هنگام اتصال یک کلاینت به آن، یک پیام خوش‌آمدگویی ارسال می‌کند:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • برخی سرویس‌ها دارای یک فرمان help هستند که اطلاعاتی دربارهٔ فرمان‌هایی که می‌توان با آن سرویس استفاده کرد، ارائه می‌دهد. در اینجا چنین نیست. فرمان‌های SMTP که در مثال استفاده شده‌اند به شرح زیر هستند:
    • mail from: expéditeur، برای مشخص کردن آدرس ایمیل فرستنده
    • rcpt to: destinataire، برای مشخص کردن آدرس ایمیل گیرنده پیام. اگر گیرندگان متعددی وجود داشته باشند، دستور rcpt to: به تعداد لازم برای هر گیرنده تکرار می‌شود.
    • data، که به سرور SMTP سیگنال می‌دهد که پیام در شرف ارسال است. همانطور که در پاسخ سرور نشان داده شده است، این شامل مجموعه‌ای از خطوط است که با خطی حاوی فقط یک نقطه پایان می‌یابد. یک پیام ممکن است دارای سربرگ‌هایی باشد که با یک خط خالی از بدنه پیام جدا شده‌اند. در مثال ما، با استفاده از کلمه کلیدی Subject: یک موضوع را گنجانده‌ایم:
  • پس از ارسال پیام، می‌توانید با استفاده از دستور 'quit' به سرور اطلاع دهید که کارتان تمام شده است. سپس سرور اتصال شبکه را قطع می‌کند. رشتهٔ خواندن می‌تواند این رویداد را تشخیص داده و متوقف شود.
  • سپس کاربر برای توقف نخ خواندن دستورات تایپ‌شده روی صفحه‌کلید، کلید «end» را فشار می‌دهد.

اگر ایمیل دریافتی را بررسی کنیم، موارد زیر را مشاهده می‌کنیم (Outlook):

Image

توجه داشته باشید که سرویس SMTP نمی‌تواند تشخیص دهد که فرستنده معتبر است یا خیر. بنابراین، فیلد from در یک پیام هرگز قابل اعتماد نیست. در این مورد، فرستنده «machin@univ-angers.fr» وجود نداشت.

این کلاینت عمومی TCP می‌تواند به ما کمک کند تا پروتکل ارتباطی مورد استفاده توسط سرویس‌های اینترنتی را شناسایی کرده و از آنجا کلاس‌های تخصصی برای کلاینت‌های این سرویس‌ها بسازیم. بیایید پروتکل ارتباطی سرویس POP (پروتکل دفتر پستی) را بررسی کنیم که به کاربران اجازه می‌دهد ایمیل‌های ذخیره‌شده خود را از روی یک سرور بازیابی کنند. این پروتکل بر روی پورت ۱۱۰ کار می‌کند.


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]

فرمان‌های اصلی به شرح زیر هستند:

  • کاربر login، جایی که جزئیات ورود خود را برای ماشینی که ایمیل‌های شما را میزبانی می‌کند وارد می‌کنید
  • pass password، که در آن رمز عبور مرتبط با ورود قبلی را وارد می‌کنید
  • list، برای دریافت فهرست پیام‌ها در قالب: شماره، اندازه به بایت
  • retr i، برای خواندن پیام شماره i
  • quit، برای پایان دادن به جلسه.

اکنون بیایید به پروتکل ارتباط بین یک کلاینت و یک وب‌سرور که معمولاً روی پورت ۸۰ اجرا می‌شود، نگاهی بیندازیم:


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]

یک کلاینت وب دستورات خود را به سرور بر اساس الگوی زیر ارسال می‌کند:

commande1
commande2
...
commanden
[ligne vide]

سرور وب تنها پس از دریافت خط خالی پاسخ می‌دهد. در این مثال، ما فقط از یک دستور استفاده کرده‌ایم:

GET /index.html HTTP/1.0

که از سرور می‌خواهد URL /index.html را ارسال کند و نشان می‌دهد که از نسخه ۱.۰ پروتکل HTTP استفاده می‌کند. جدیدترین نسخه این پروتکل ۱.۱ است. این مثال نشان می‌دهد که سرور با بازگرداندن محتوای فایل index.html پاسخ داده و سپس اتصال را قطع کرده است، همان‌طور که می‌توانیم ببینیم نخ خواندن پاسخ‌ها متوقف شده است. قبل از ارسال محتوای فایل index.html، وب‌سرور مجموعه‌ای از سربرگ‌ها را ارسال کرد که با یک خط خالی پایان یافت:

<-- 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> اولین خط فایل /index.html است. خطوط قبلی به عنوان سرآیندهای HTTP (پروتکل انتقال HyperText) شناخته می‌شوند. در اینجا وارد جزئیات این سربرگ‌ها نمی‌شویم، اما شایان ذکر است که کلاینت عمومی ما به آن‌ها دسترسی می‌دهد که ممکن است برای درک آن‌ها مفید باشد. به‌عنوان مثال، خط اول را در نظر بگیرید:

<-- HTTP/1.1 200 OK

نشان می‌دهد که سرور وب تماس‌گرفته شده از پروتکل HTTP/1.1 پشتیبانی می‌کند و با موفقیت فایل درخواستی (200 OK) را پیدا کرده است، که در آن 200 یک کد پاسخ HTTP است. خطوط

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

به کلاینت اطلاع می‌دهد که ۱۱٬۲۵۱ بایت حاوی متن در HTML (زبان نشانه‌گذاری HyperText) دریافت خواهد کرد و اینکه پس از اتمام انتقال، اتصال بسته خواهد شد.

پس در اینجا یک کلاینت TCP بسیار کاربردی داریم. این کلاینت بی‌شک کمتر از برنامه telnet که قبلاً استفاده کردیم انجام می‌دهد، اما نوشتن آن به دست خودمان جالب بود. برنامه کلی کلاینت TCP به شرح زیر است:

// پکیج‌های وارداتی
import java.io.*;
import java.net.*;

public class clientTCPgenerique{

    // ویژگی‌های یک سرویس را به‌عنوان پارامتر در قالب می‌گیرد
     // پورت سرور
     //به سرویس متصل می‌شود
     // یک نخ ایجاد می‌کند تا دستورات تایپ‌شده روی صفحه‌کلید را بخواند
     //این‌ها به سرور ارسال خواهند شد
     // یک نخ ایجاد می‌کند تا پاسخ‌ها را از سرور بخواند
     //این‌ها روی صفحه نمایش داده می‌شوند
     //کل فرآیند زمانی پایان می‌یابد که دستور «end» تایپ شود

   // متغیر نمونه
  private static Socket client;

    public static void main(String[] args){

         // سینتکس
        final String syntaxe="pg serveur port";

         // تعداد آرگومان‌ها
        if(args.length != 2)
            erreur(syntaxe,1);

         // توجه به نام سرور
        String serveur=args[0];

         // پورت باید یک عدد صحیح مثبت باشد
        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;
         //ممکن است مشکلاتی وجود داشته باشد
        try{
             // اتصال به سرویس
            client=new Socket(serveur,port);
        }catch(Exception ex){
             // خطا
            erreur("Impossible de se connecter au service ("+ serveur
                +","+port+"), erreur : "+ex.getMessage(),3);
             //پایان
            return;
        }//catch

         // رشته‌های خواندن/نوشتن ایجاد می‌شوند
    new ClientSend(client).start();
    new ClientReceive(client).start();

        // پایان نخ اصلی
        return;
    }// اصلی

     //نمایش خطاها
    public static void erreur(String msg, int exitCode){
         // نمایش خطا
        System.err.println(msg);
         //خاتمه با خطا
        System.exit(exitCode);
    }//خطا
}//کلاس  

class ClientSend extends Thread {
    //کلاس مسئول خواندن دستورات صفحه‌کلید
     // و ارسال آن‌ها به یک سرور از طریق یک کلاینت TCP که به‌عنوان پارامتر ارسال شده است

    private Socket client;    // کلاینت TCP

     // سازنده
    public ClientSend(Socket client){
         //توجه به کلاینت TCP
        this.client=client;
    }//

     // متد Runِ نخ
    public void run(){

        // داده‌های محلی
        PrintWriter OUT=null;            //جریان نوشتن شبکه
    BufferedReader IN=null;        //جریان صفحه‌کلید
        String commande=null;            // دستور خوانده شده از صفحه‌کلید

         // پردازش خطا
        try{
             //ایجاد جریان نوشتن شبکه
            OUT=new PrintWriter(client.getOutputStream(),true);
      //ایجاد جریان ورودی صفحه‌کلید
      IN=new BufferedReader(new InputStreamReader(System.in));
            //حلقه ورودی و ارسال فرمان
            System.out.println("Commandes : ");
            while(true){
                 //خواندن دستوری که روی صفحه‌کلید تایپ شده است
                commande=IN.readLine().trim();
                // تمام شد؟
                if (commande.toLowerCase().equals("fin")) break;
                //ارسال فرمان به سرور
                OUT.println(commande);
                 // دستور بعدی
            }//در حالی که
        }catch(Exception ex){
             //خطا
            System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
        }//گرفت
         //پایان – بستن استریم‌ها
        try{
            OUT.close();client.close();
        }catch(Exception ex){}
         // نشان‌دهنده پایان نخ
        System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
    }//اجرا
}//کلاس

class ClientReceive extends Thread{
    //کلاسی که مسئول خواندن خطوط متنی است که برای یک 
     // کلاینت TCP به‌عنوان پارامتر ارسال شده است

    private Socket client;    // کلاینت TCP

     // سازنده
    public ClientReceive(Socket client){
         // توجه شود که کلاینت TCP
        this.client=client;
    }//

     // متد Runِ نخ
    public void run(){

        // داده‌های محلی
        BufferedReader IN=null;        //جریان خواندن شبکه
        String réponse=null;        // پاسخ سرور

         // مدیریت خطا
        try{
             //ایجاد جریان خواندن شبکه
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            //حلقه برای خواندن خطوط متن از جریان IN
            while(true){
                 // خواندن جریان شبکه
                réponse=IN.readLine();
                 //آیا جریان بسته است؟
                if(réponse==null) break;
                //نمایش
                System.out.println("<-- "+réponse);
            }//در حالی که
        }catch(Exception ex){
            //خطا
            System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
        }//گرفتن
         // پایان – بستن جریان‌ها
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // نشان‌دهنده پایان نخ
        System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
    }//اجرا
}//کلاس

9.2. سرور عمومی TCP

اکنون به یک سرور نگاه می‌کنیم

  • که دستورات ارسال‌شده توسط کلاینت‌های خود را روی صفحه نمایش می‌دهد
  • و در پاسخ، خطوط متنی را که کاربر از طریق صفحه‌کلید تایپ کرده است، برای آن‌ها ارسال می‌کند. بنابراین این کاربر است که نقش سرور را ایفا می‌کند.

برنامه با استفاده از دستور زیر اجرا می‌شود: java serveurTCPgenerique portEcoute، که در آن portEcoute پورتی است که کلاینت‌ها باید به آن متصل شوند. سرویس کلاینت توسط دو نخ (thread) مدیریت خواهد شد:

  • یک تِرد که منحصراً به خواندن خطوط متنی ارسال‌شده توسط کلاینت اختصاص دارد
  • یک تِرد اختصاصی که صرفاً به خواندن پاسخ‌های تایپ‌شده توسط کاربر می‌پردازد. این تِرد با استفاده از دستور 'fin' سیگنال پایان اتصال با کلاینت را ارسال می‌کند.

سرور برای هر کلاینت دو تِرد ایجاد می‌کند. اگر n کلاینت وجود داشته باشد، در یک زمان 2n تِرد فعال خواهند بود. خودِ سرور هرگز متوقف نمی‌شود، مگر اینکه کاربر کلیدهای Ctrl-C را روی صفحه‌کلید فشار دهد. بیایید چند مثال را بررسی کنیم.

سرور روی پورت ۱۰۰ در حال اجرا است و ما از کلاینت عمومی برای ارتباط با آن استفاده می‌کنیم. پنجره کلاینت به این شکل است:

E:\data\serge\MSNET\c#\network\generic TCP client> 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]

خطوطی که با <-- شروع می‌شوند، از سمت سرور به کلاینت ارسال شده‌اند؛ سایر خطوط از سمت کلاینت به سرور ارسال شده‌اند. پنجرهٔ سرور به شرح زیر است:


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]

خطوطی که با <-- شروع می‌شوند، ارسال‌شده از کلاینت به سرور هستند. خطوطی که با N: علامت‌گذاری شده‌اند، ارسال‌شده از سرور به کلاینت شماره N هستند. سرور بالا هنوز فعال است، در حالی که کلاینت ۱ کارش را تمام کرده است. یک کلاینت دوم برای همان سرور راه‌اندازی می‌شود:


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]

پنجرهٔ سرور سپس به این شکل به نظر می‌رسد:


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

اکنون بیایید با اجرای سرور عمومی خود روی پورت 88، یک وب‌سرور را شبیه‌سازی کنیم:


Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88

حالا یک مرورگر را باز کرده و صفحه http://localhost:88/exemple.html را درخواست کنیم. سپس مرورگر به پورت 88 روی ماشین localhost متصل شده و صفحه /exemple.html را درخواست می‌کند:

Image

حال بیایید به پنجرهٔ سرور خود نگاه کنیم:

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

بدین ترتیب می‌توانیم سربرگ‌های HTTP ارسال‌شده توسط مرورگر را مشاهده کنیم. این امر به ما امکان می‌دهد تا پروتکل HTTP را به تدریج کشف کنیم. در مثال قبلی، ما یک کلاینت وب ایجاد کردیم که تنها فرمان واحد GET را ارسال می‌کرد. این کافی بود. در اینجا می‌بینیم که مرورگر اطلاعات بیشتری را برای سرور ارسال می‌کند. هدف از این کار، اطلاع‌رسانی به سرور در مورد نوع کلاینتی است که با آن سر و کار دارد. همچنین می‌بینیم که هدرهای HTTP با یک خط خالی به پایان می‌رسند.

بیایید برای کلاینت خود یک پاسخ بسازیم. کاربر پشت کیبورد در این مورد، خود سرور است و می‌تواند پاسخ را به‌صورت دستی بسازد. بیایید پاسخ تولید شده توسط یک وب‌سرور را در مثال قبلی به یاد بیاوریم:

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

بیایید سعی کنیم پاسخ مشابهی ارائه دهیم:

...
<-- 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 شروع می‌شوند از سرور به کلاینت شماره 2 ارسال می‌شوند. دستور fin اتصال بین سرور و کلاینت را خاتمه می‌دهد. در پاسخ خود، ما خود را به سربرگ‌های زیر HTTP محدود کرده‌ایم:

HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :

ما اندازه فایلی را که قصد ارسال آن را داریم (Content-Length) مشخص نمی‌کنیم، بلکه صرفاً اعلام می‌کنیم که پس از ارسال آن، اتصال را بست خواهیم کرد (Connection: close). این برای مرورگر کافی است. وقتی مرورگر ببیند که اتصال بسته شده است، متوجه می‌شود که پاسخ سرور کامل است و صفحه‌ای را که برایش ارسال شده (HTML) نمایش می‌دهد. این صفحه به شرح زیر است:

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>

کاربر سپس با وارد کردن دستور fin اتصال به کلاینت را قطع می‌کند. مرورگر سپس می‌داند که پاسخ سرور کامل است و می‌تواند آن را نمایش دهد:

Image

اگر، همانطور که در بالا نشان داده شده است، دستور View/Source را وارد کنیم تا ببینیم مرورگر چه چیزی دریافت کرده است، با خروجی زیر مواجه می‌شویم:

Image

یعنی دقیقاً همان چیزی که از سرور عمومی ارسال شده بود.

کد سرور عمومی TCP به شرح زیر است:

// پکیجها
import java.io.*;
import java.net.*;

public class serveurTCPgenerique{

    // برنامهٔ اصلی
    public static void main (String[] args){

    // روی پورت برای درخواستهای کلاینت گوش میدهد
     //یک رشته ایجاد میکند تا درخواستهای مشتری را بخواند
     //اینها روی صفحه نمایش داده خواهند شد
     // یک رشته برای خواندن دستورات وارد شده از طریق صفحهکلید ایجاد میکند
     //اینها بهعنوان پاسخ به کلاینت ارسال خواهند شد
     // فرآیند زمانی پایان مییابد که دستور 'end' از طریق صفحهکلید وارد شود

    final String syntaxe="Syntaxe : pg port";
   // متغیر نمونه
         //آیا آرگومانی وجود دارد
     if(args.length != 1)
        erreur(syntaxe,1);

         // پورت باید یک عدد صحیح بزرگتر از 0 باشد
        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);

     // سرویس گوش دادن ایجاد میشود
    ServerSocket ecoute=null;
    int nbClients=0;    // تعداد مشتریان پردازششده
        try{
             // سرویس ایجاد میشود
            ecoute=new ServerSocket(port);
             // نظارت
            System.out.println("Serveur générique lancé sur le port " + port);

             // حلقه سرویس مشتری
            Socket client=null;
            while (true){ //حلقه بینهایت  با Ctrl-C متوقف میشود
                 // در انتظار مشتری
                client=ecoute.accept();

                 // سرویس توسط نخهای جداگانه اجرا میشود
                nbClients++;

                 // رشتههای خواندن/نوشتن ایجاد میشوند
        new ServeurSend(client,nbClients).start();
        new ServeurReceive(client,nbClients).start();

                //بازگشت به شنود درخواستها
            }// پایان حلقه while
        }catch(Exception ex){
             //خطا گزارش میشود
            erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
        }//catch
    }// پایان برنامه اصلی

     // نمایش خطاها
    public static void erreur(String msg, int exitCode){
         // نمایش خطا
        System.err.println(msg);
         // خاموشی با خطا
        System.exit(exitCode);
    }//خطا
}//کلاس

class ServeurSend extends Thread{
    //کلاس مسئول خواندن ورودی صفحهکلید
     // و ارسال آنها به یک کلاینت از طریق یک کلاینت TCP که به سازنده پاس داده شده است

    Socket client;    // کلاینت TCP
    int numClient;        // شماره مشتری

     // سازنده
    public ServeurSend(Socket client, int numClient){
         // توجه به کلاینت TCP
        this.client=client;
         // و شماره آن
        this.numClient=numClient;
    }//سازنده

     // متد Runِ نخ
    public void run(){

        // دادههای محلی
        PrintWriter OUT=null;        //جریان نوشتن شبکه
        String réponse=null;        // ورودی صفحهکلید
    BufferedReader IN=null;    //جریان صفحهکلید

        // ردیابی
        System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
         // مدیریت خطا
        try{
             // ایجاد جریان نوشتن شبکه
            OUT=new PrintWriter(client.getOutputStream(),true);
      // ایجاد جریان صفحهکلید
      IN=new BufferedReader(new InputStreamReader(System.in));
            //حلقهٔ ورودی و ارسال فرمان
            while(true){
                 //شناسایی مشتری
                System.out.print("--> " + numClient + " : ");
                 // خواندن پاسخ تایپشده روی صفحهکلید
                réponse=IN.readLine().trim();
                // تمام شد؟
                if (réponse.toLowerCase().equals("fin")) break;
                //ارسال پاسخ به سرور
                OUT.println(réponse);
                 // پاسخ بعدی
            }//در حالی که
        }catch(Exception ex){
             // خطا
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         //پایان  بستن جریانها
        try{
            OUT.close();client.close();
        }catch(Exception ex){}
         // نشاندهنده پایان نخ
        System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
    }//اجرا
}//کلاس

class ServeurReceive extends Thread{
    //کلاسی که مسئول خواندن خطوط متنی ارسالشده به سرور است 
     // از طریق یک کلاینت TCP که به سازنده پاس داده شده است

    Socket client;    // کلاینت TCP
    int numClient;        // شماره مشتری

     // سازنده
    public ServeurReceive(Socket client, int numClient){
         // توجه به مشتری TCP
        this.client=client;
         //و شماره آن
        this.numClient=numClient;
    }//سازنده

     // متد Runِ نخ
    public void run(){

        // دادههای محلی
        BufferedReader IN=null;        //جریان خواندن شبکه
        String réponse=null;        // پاسخ سرور

         // ردیابی
        System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
         // مدیریت خطا
        try{
             // ایجاد جریان خواندن شبکه
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            //حلقه برای خواندن خطوط متن از جریان IN
            while(true){
                 //خواندن جریان شبکه
                réponse=IN.readLine();
                 //آیا جریان بسته است؟
                if(réponse==null) break;
                //نمایش
                System.out.println("<-- "+réponse);
            }//در حالی که
        }catch(Exception ex){
            // خطا
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
        }//گرفتن
         //پایان  جریانها بسته شدهاند
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // نشاندهنده پایان نخ
        System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
    }//اجرا
}//کلاس