Skip to content

8. Додатки

8.1. Інструменти веб-розробки

Тут ми вказуємо, де знайти та як встановити інструменти, необхідні для веб-розробки. Деякі інструменти зазнали оновлень, і можливі випадки, коли наведені тут пояснення вже не відповідатимуть найновішим версіям. У такому разі читачеві доведеться адаптуватися... У курсі веб-програмування ми будемо використовувати переважно такі інструменти, усі з яких доступні безкоштовно:

  • сучасний веб-браузер, здатний відображати XML. Приклади курсу було перевірено в Internet Explorer 6.
  • сучасний JDK (Java Development Kit). Приклади курсу було протестовано з JDK 1.4. Цей JDK містить плагін Java 1.4 для браузерів, що дозволяє їм відображати Java-аплети, які використовують JDK 1.4.
  • середовище розробки Java для написання Java-сервлетів. У даному випадку це JBuilder 7.
  • веб-сервери: Apache, PWS (Personal Web Server), Tomcat.
    • Apache використовуватиметься для розробки веб-додатків на PERL (Practical Extracting and Reporting Language) або PHP (Personal Home Page)
    • PWS використовуватиметься для розробки веб-додатків на мові ASP (Active Server Pages) або PHP
    • Tomcat використовуватиметься для розробки веб-додатків за допомогою Java-сервлетів або сторінок JSP (Java Server Pages)
  • додаток для управління базою даних: MySQL
  • EasyPHP: інструмент, що об’єднує веб-сервер Apache, мову PHP та SGBD MySQL

8.1.1. Веб-сервери, браузери, мови скриптів

  1. Основні веб-сервери
    • Apache (Linux, Windows)
    • Interner Information Server IIS (NT), Personal Web Server PWS (Windows 9x)
  1. Основні веб-браузери
    • Internet Explorer (Windows)
    • Netscape (Linux, Windows)
  1. Серверні мови сценаріїв
    • VBScript (IIS, PWS)
    • JavaScript (IIS, PWS)
    • Perl (Apache, IIS, PWS)
    • PHP (Apache, IIS, PWS)
    • Java (Apache, Tomcat)
    • Мови .NET
  1. Мови скриптів на стороні браузера
    • VBScript (IE)
    • JavaScript (IE, Netscape)
    • PerlScript (IE)
    • Java (IE, Netscape)

8.1.2. Де знайти інструменти

Netscape
http://www.netscape.com/ (посилання на завантаження)
Internet Explorer
http://www.microsoft.com/windows/ie/default.asp
PHP
http://www.php.net
http://www.php.net/downloads.php (бінарні файли для Windows)
PERL
http://www.activestate.com http://www.activestate.com/Products/
http://www.activestate.com/Products/ActivePerl/
Vbscript, Javascript
http://msdn.microsoft.com/scripting (перейдіть за посиланням на скрипт Windows)
JAVA
http://java.sun.com/
http://java.sun.com/downloads.html (JSE)
http://java.sun.com/j2se/1.4/download.html
Apache
http://www.apache.org/
http://www.apache.org/dist/httpd/binaries/win32/
PWS
входить до складу NT 4.0 Option pack для Windows 95
входить до складу CD для Windows 98
http://www.microsoft.com/ntserver/nts/downloads/recommended/NT4OptPk/win95.asp
IIS 
(windows NT/2000)
http://www.microsoft.com
Tomcat
http://jakarta.apache.org/tomcat/
JBuilder
http://www.borland.com/jbuilder/
http://www.borland.com/products/downloads/download_jbuilder.html
EasyPHP
http://www.easyphp.org/
http://www.easyphp.org/telechargements.php3

8.1.3. EasyPHP

Ця програма дуже зручна тим, що об’єднує в одному пакеті:

  • веб-сервер Apache (1.3.x)
  • мову PHP (4.x)
  • SGBD MySQL (3.23.x)
  • інструмент адміністрування MySQL: PhpMyAdmin

Інсталяційний файл має такий вигляд:

Image

Встановлення EasyPHP проходить без проблем, і у файловій системі створюється дерево папок:

Image

easyphp.exe
виконуваний файл програми
apache
дерево каталогів сервера Apache
mysql
дерево каталогів MySQL SGBD
phpmyadmin
дерево папок додатка phpMyAdmin
php
дерево php
www
коренева папка дерева веб-сторінок, що видаються сервером Apache з EasyPHP
cgi-bin
дерево, в якому можна розміщувати скрипти CGI для сервера Apache

Головна перевага EasyPHP полягає в тому, що програма поставляється у попередньо налаштованому вигляді. Таким чином, Apache, PHP та MySQL вже налаштовані для спільної роботи. Коли запускати EasyPhp за посиланням у меню програм, у правому нижньому куті екрана з’являється піктограма.

Саме літера «E» з червоною крапкою має блимати, якщо веб-сервер Apache та база даних MySQL працюють. При натисканні на неї правою кнопкою миші відкриваються опції меню:

Image

Опція Administration дозволяє виконувати налаштування та перевіряти працездатність:

Image

8.1.3.1. Адміністрування PHP

Кнопка «Інформація про PHP» дозволяє перевірити працездатність комбінації Apache-PHP: має з’явитися інформаційна сторінка PHP:

Image

Кнопка «Розширення» відображає список встановлених розширень для PHP. Насправді це бібліотеки функцій.

Image

На екрані вище, наприклад, показано, що функції, необхідні для використання бази даних MySQL, дійсно присутні.

Кнопка «Параметри» відображає login/motdepasse адміністратора бази даних MySQL.

Image

Використання бази даних MySQL виходить за межі цього короткого огляду, але тут очевидно, що адміністратору бази даних слід встановити пароль.

8.1.3.2. Адміністрування Apache

Також на сторінці адміністрування EasyPHP посилання «Ваші псевдоніми» дозволяє визначити псевдоніми, пов’язані з певним каталогом. Це дає змогу розміщувати веб-сторінки не лише в каталозі www дерева каталогів easyPhp.

Image

Якщо на цій сторінці ввести таку інформацію:

Image

і натиснути кнопку valider, до файлу <easyphp>\apache\conf\httpd.conf додадуться такі рядки:

    Alias /st/ "e:/data/serge/web/"

    <Directory "e:/data/serge/web">
        Options FollowSymLinks Indexes
        AllowOverride None
        Order deny,allow
        allow from 127.0.0.1
        deny from all
    </Directory>

<easyphp> позначає каталог інсталяції EasyPHP. httpd.conf — це файл конфігурації сервера Apache. Отже, те саме можна зробити, редагуючи цей файл безпосередньо. Зміни у файлі httpd.conf зазвичай одразу враховуються сервером Apache. Якщо це не сталося, слід зупинити сервер, а потім запустити його знову, використовуючи іконку easyphp:

Image

Щоб завершити наш приклад, тепер можна розмістити веб-сторінки в дереві каталогів e:\data\serge\web:

C:\winnt\system32>dir e:\data\serge\web\html\balises.htm

14/07/2002  17:02                3 767 balises.htm

та завантажити цю сторінку, використовуючи псевдонім st:

Image

У цьому прикладі сервер Apache налаштовано на роботу на порту 81. Його порт за замовчуванням — 80. Цей параметр контролюється наступним рядком у файлі httpd.conf, з яким ми вже знайомилися:

Port 81

8.1.3.3. Конфігураційний файл Apache htpd.conf

Якщо потрібно дещо доопрацювати налаштування Apache, доводиться «вручну» редагувати його файл конфігурації httpd.conf, розташований у папці <easyphp>\apache\conf:

Image

Ось кілька моментів, на які варто звернути увагу в цьому конфігураційному файлі:

ligne(s)
роль
ServerRoot "D:/Program Files/Apache Group/Apache"
вказує папку, в якій знаходиться дерево каталогів Apache
Port 80
вказує, на якому порту працюватиме веб-сервер. Зазвичай це 80. Змінивши цей рядок, можна налаштувати веб-сервер на роботу на іншому порту
ServerAdmin root@istia.univ-angers.fr
адреса електронної пошти адміністратора сервера Apache
ServerName stahe.istia.uang
ім'я комп'ютера, на якому «працює» сервер Apache
ServerRoot "E:/Program Files/EasyPHP/apache"
каталог, у якому встановлено сервер Apache. Коли у файлі конфігурації з’являються відносні імена файлів, вони є відносними саме щодо цього каталогу.
DocumentRoot "E:/Program Files/EasyPHP/www"
коренева папка дерева веб-сторінок, що видаються сервером. Тут URL-адреса http://machine/rep1/fic1.html відповідатиме файлу E:\Program Files\EasyPHP\www\rep1\fic1.html
<Directory "E:/Program Files/EasyPHP/www">
визначає властивості попередньої папки
ErrorLog logs/error.log
папка з логами, тобто фактично <ServerRoot>\logs\error.log: E:\Program Files\EasyPHP\apache\logs\error.log. Саме цей файл слід перевірити, якщо ви помітили, що сервер Apache не працює.
    ScriptAlias /cgi-bin/ "E:/Program Files/EasyPHP/cgi-bin/"
E:\Program Files\EasyPHP\cgi-bin — це кореневий каталог дерева, куди можна розміщувати скрипти CGI. Таким чином, URL http://machine/cgi-bin/rep1/script1.pl буде URL-адресою скрипта CGI E:\Program Files\EasyPHP\cgi-bin\rep1\script1.pl.
<Directory "E:/Program Files/EasyPHP/cgi-bin/">
визначає властивості вищезазначеної папки
LoadModule php4_module "E:/Program
 Files/EasyPHP/php/php4apache.dll"
AddModule mod_php4.c
рядки завантаження модулів, що дозволяють Apache працювати з PHP4.
    AddType application/x-httpd-php
 .phtml .pwml .php3 
.php4 .php .php2 .inc
визначає розширення файлів, які слід розглядати як файли, що мають оброблятися PHP

8.1.3.4. Адміністрування MySQL за допомогою PhpMyAdmin

На сторінці адміністрування EasyPhp натисніть кнопку PhpMyAdmin:

Image

У випадаючому списку під Accueil можна переглянути бази
.
Число в дужках — це кількість таблиць. Якщо
вибрати базу даних, то відобразяться таблиці з неї:

Веб-сторінка пропонує низку операцій з базою даних:

Image

Якщо натиснути на посилання Afficher у user:

Image

Тут є лише один користувач: root, який є адміністратором MySQL. Перейшовши за посиланням Modifier, можна змінити його пароль, який наразі порожній, що не рекомендується для адміністратора.

Ми не будемо детальніше зупинятися на PhpMyAdmin, оскільки це багатофункціональне програмне забезпечення, яке заслуговує на опис обсягом у кілька сторінок.

8.1.4. PHP

Ми вже розглянули, як отримати PHP за допомогою програми EasyPhp. Щоб отримати PHP безпосередньо, перейдіть на сайт http://www.php.net.

PHP можна використовувати не лише в веб-середовищі. Його також можна застосовувати як мову скриптів у Windows. Створіть наступний скрипт і збережіть його під назвою date.php:

<?
   // скрипт PHP, що відображає час
  $maintenant=date("j/m/y, H:i:s",time());
  echo "Nous sommes le $maintenant";
?>

У вікні DOS перейдіть до каталогу date.php і запустіть його:

E:\data\serge\php\essais>"e:\program files\easyphp\php\php.exe" date.php
X-Powered-By: PHP/4.2.0
Content-type: text/html

Nous sommes le 18/07/02, 09:31:01

8.1.5. PERL

Бажано, щоб Internet Explorer вже був встановлений. Якщо він є, Active Perl налаштує його так, щоб він підтримував скрипти PERL на сторінках HTML, які будуть виконуватися самим IE на стороні клієнта. Веб-сайт Active Perl знаходиться за адресою URL http://www.activestate.comA. Після встановлення PERL буде розміщено в каталозі, який ми назвемо <perl>. Він містить таку структуру:

DEISL1   ISU        32 403  23/06/00  17:16 DeIsL1.isu
BIN            <REP>        23/06/00  17:15 bin
LIB            <REP>        23/06/00  17:15 lib
HTML           <REP>        23/06/00  17:15 html
EG             <REP>        23/06/00  17:15 eg
SITE           <REP>        23/06/00  17:15 site
HTMLHELP       <REP>        28/06/00  18:37 htmlhelp

Виконуваний файл perl.exe знаходиться в папці <perl>\bin. Perl — це мова скриптів, що працює в системах Windows та Unix. Крім того, вона використовується в програмуванні WEB. Напишемо перший скрипт:

# скрипт PERL, що відображає час

# модулі
use strict;

# програма
my ($secondes,$minutes,$heure)=localtime(time);
print "Il est $heure:$minutes:$secondes\n";

Збережіть цей скрипт у файлі heure.pl. Відкрийте вікно DOS, перейдіть до каталогу з попереднім скриптом і запустіть його:

E:\data\serge\Perl\Essais>e:\perl\bin\perl.exe heure.pl
Il est 9:34:21

8.1.6. Vbscript, Javascript, Perlscript

Ці мови є мовами скриптів для Windows. Вони можуть працювати в різних середовищах, таких як

  • Windows Scripting Host для безпосереднього використання в Windows, зокрема для написання скриптів системного адміністрування
  • Internet Explorer. У цьому випадку він використовується на сторінках HTML, надаючи їм певну інтерактивність, якої неможливо досягти лише за допомогою мови HTML.
  • Internet Information Server (IIS) — веб-сервер Microsoft для NT/2000 та його аналог Personal Web Server (PWS) для Win9x. У цьому випадку для програмування на стороні веб-сервера використовується VBScript — технологія, яку Microsoft називає ASP (Active Server Pages).

Файл інсталяції можна завантажити за посиланням URL: http://msdn.microsoft.com/scripting, після чого слід перейти за посиланнями «Windows Script». Встановлюються:

  • контейнер Windows Scripting Host, що дозволяє використовувати різні мови скриптів, такі як Vbscript і Javascript, а також інші, наприклад PerlScript, що входить до складу Active Perl.
  • інтерпретатор VBscript
  • інтерпретатор Javascript

Проведемо кілька швидких тестів. Створимо таку програму vbscript:

' клас
class personne
  Dim nom
  Dim age
End class

' створення об’єкта «особа»
Set p1=new personne
With p1
  .nom="dupont"
  .age=18
End With

' відображення властивостей особи p1
With p1
  wscript.echo "nom=" & .nom
  wscript.echo "age=" & .age
End With

Ця програма використовує об’єкти. Назвемо її objets.vbs (суфікс vbs позначає файл VBScript). Перейдемо до каталогу, в якому вона знаходиться, і запустимо її:

E:\data\serge\windowsScripting\vbscript\poly\objets>cscript objets.vbs
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

nom=dupont
age=18

Тепер давайте створимо таку програму на JavaScript, яка використовує масиви:

// масив у варіанті
// порожній масив
tableau=new Array();
affiche(tableau);
// масив динамічно збільшується
for(i=0;i<3;i++){
  tableau.push(i*10);
}
// відображення масиву
affiche(tableau);
// ще
for(i=3;i<6;i++){
  tableau.push(i*10);
}
affiche(tableau);

// багатовимірні масиви
WScript.echo("-----------------------------");

tableau2=new Array();
for(i=0;i<3;i++){
  tableau2.push(new Array());
  for(j=0;j<4;j++){
    tableau2[i].push(i*10+j);
  }//for j
}// for i
affiche2(tableau2);

// кінець
WScript.quit(0);

// ---------------------------------------------------------
function affiche(tableau){
  // відображення масиву
  for(i=0;i<tableau.length;i++){
    WScript.echo("tableau[" + i + "]=" + tableau[i]);
  }//для
}//функція

// ---------------------------------------------------------
function affiche2(tableau){
   // відображення таблиці
  for(i=0;i<tableau.length;i++){
    for(j=0;j<tableau[i].length;j++){
      WScript.echo("tableau[" + i + "," + j + "]=" + tableau[i][j]);
    }// для j
  }//для i
}//функція

Ця програма використовує масиви. Назвемо її tableaux.js (суфікс js позначає файл JavaScript). Перейдемо до каталогу, в якому вона знаходиться, і запустимо її:

E:\data\serge\windowsScripting\javascript\poly\tableaux>cscript tableaux.js
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. Tous droits réservés.

tableau[0]=0
tableau[1]=10
tableau[2]=20
tableau[0]=0
tableau[1]=10
tableau[2]=20
tableau[3]=30
tableau[4]=40
tableau[5]=50
-----------------------------
tableau[0,0]=0
tableau[0,1]=1
tableau[0,2]=2
tableau[0,3]=3
tableau[1,0]=10
tableau[1,1]=11
tableau[1,2]=12
tableau[1,3]=13
tableau[2,0]=20
tableau[2,1]=21
tableau[2,2]=22
tableau[2,3]=23

Наостанок — ще один приклад на Perlscript. Щоб отримати доступ до Perlscript, потрібно встановити Active Perl.

<job id="PERL1">
  <script language="PerlScript">
       # класичного Perl
    %dico=("maurice"=>"juliette","philippe"=>"marianne");
    @cles= keys %dico;
    for ($i=0;$i<=$#ключі;$i++){
        $cle=$cles[$i];
      $valeur=$dico{$cle};
        $WScript->echo ("clé=".$cle.", valeur=".$valeur);
    }
     # на PerlScript із використанням об’єктів Windows Script
    $dico=$WScript->CreateObject("Scripting.Dictionary");
    $dico->add("maurice","juliette");
    $dico->add("philippe","marianne");
    $WScript->echo($dico->item("maurice"));
    $WScript->echo($dico->item("philippe"));    
  </script>
</job>

Ця програма демонструє створення та використання двох словників: одного у класичному стилі Perl, іншого — з використанням об’єкта Scripting Dictionary у Windows Script. Збережемо цей код у файлі dico.wsf (wsf — це розширення файлів Windows Script). Перейдемо до папки з цією програмою та запустимо її:

E:\data\serge\windowsScripting\perlscript\essais>cscript dico.wsf
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. Tous droits réservés.

clé=philippe, valeur=marianne
clé=maurice, valeur=juliette
juliette
marianne

Perlscript може використовувати об'єкти контейнера, в якому він виконується. У даному випадку це були об'єкти контейнера Windows Script. У контексті веб-програмування скрипти VBscript, JavaScript, Perlscript можуть виконуватися або в браузері IE, або на сервері PWS чи IIS. Якщо скрипт є дещо складним, доцільно протестувати його поза веб-контекстом, у контейнері Windows Script, як це було розглянуто раніше. Таким чином можна протестувати лише ті функції скрипта, які не використовують об’єкти, властиві браузеру чи серверу. Навіть з цим обмеженням така можливість залишається цікавою, оскільки, як правило, налагоджувати скрипти, що виконуються на веб-серверах або в браузерах, досить незручно.

8.1.7. JAVA

Java доступна за адресою URL: http://www.sun.com і встановлюється в каталозі, який ми назвемо <java>, що містить такі елементи:

22/05/2002  05:51       <DIR>          .
22/05/2002  05:51       <DIR>          ..
22/05/2002  05:51       <DIR>          bin
22/05/2002  05:51       <DIR>          jre
07/02/2002  12:52                8 277 README.txt
07/02/2002  12:52               13 853 LICENSE
07/02/2002  12:52                4 516 COPYRIGHT
07/02/2002  12:52               15 290 readme.html
22/05/2002  05:51       <DIR>          lib
22/05/2002  05:51       <DIR>          include
22/05/2002  05:51       <DIR>          demo
07/02/2002  12:52           10 377 848 src.zip
11/02/2002  12:55       <DIR>          docs

У папці bin знайдемо javac.exe — компілятор Java та java.exe — віртуальну машину Java. Можна виконати такі тести:

  1. Написати такий скрипт:
//програма на Java, що відображає час

import java.io.*;
import java.util.*;

public class heure{
  public static void main(String arg[]){
     // отримання дати та часу
     Date maintenant=new Date();
      // виведення на екран
     System.out.println("Il est "+maintenant.getHours()+
        ":"+maintenant.getMinutes()+":"+maintenant.getSeconds());
  }//main
}//клас
  1. Збережіть цю програму під назвою heure.java. Відкрийте вікно DOS. Перейдіть до каталогу, де знаходиться файл heure.java, і скомпілюйте його:
D:\data\java\essais>c:\jdk1.3\bin\javac heure.java
Note: heure.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.

У наведеному вище командному рядку c:\jdk1.3\bin\javac слід замінити на точний шлях до компілятора javac.exe. У тому ж каталозі, що й heure.java, має з’явитися файл heure.class — це програма, яку тепер запустить віртуальна машина java.exe.

  1. Запустіть програму:
D:\data\java\essais>c:\jdk1.3\bin\java heure
Il est 10:44:2

8.1.8. Сервер Apache

Ми вже бачили, що сервер Apache можна отримати за допомогою програми EasyPhp. Щоб завантажити його безпосередньо, перейдемо на сайт Apache: http://www.apache.org. Під час інсталяції створюється дерево папок, у якому містяться всі необхідні для роботи сервера файли. Назвемо цей каталог <apache>. Він містить дерево папок, подібне до наведеного нижче:

UNINST   ISU       118 805  23/06/00  17:09 Uninst.isu
HTDOCS         <REP>        23/06/00  17:09 htdocs
APACHE~1 DLL       299 008  25/02/00  21:11 ApacheCore.dll
ANNOUN~1             3 000  23/02/00  16:51 Announcement
ABOUT_~1            13 197  31/03/99  18:42 ABOUT_APACHE
APACHE   EXE        20 480  25/02/00  21:04 Apache.exe
KEYS                36 437  20/08/99  11:57 KEYS
LICENSE              2 907  01/01/99  13:04 LICENSE
MAKEFI~1 TMP        27 370  11/01/00  13:47 Makefile.tmpl
README               2 109  01/04/98   6:59 README
README   NT          3 223  19/03/99   9:55 README.NT
WARNIN~1 TXT           339  21/09/98  13:09 WARNING-NT.TXT
BIN            <REP>        23/06/00  17:09 bin
MODULES        <REP>        23/06/00  17:09 modules
ICONS          <REP>        23/06/00  17:09 icons
LOGS           <REP>        23/06/00  17:09 logs
CONF           <REP>        23/06/00  17:09 conf
CGI-BIN        <REP>        23/06/00  17:09 cgi-bin
PROXY          <REP>        23/06/00  17:09 proxy
INSTALL  LOG         3 779  23/06/00  17:09 install.log
conf
папка з файлами конфігурації Apache
logs
папка з файлами журналів (моніторингу) Apache
bin
виконувані файли Apache

8.1.8.1. Configuration

У папці <Apache>\conf містяться такі файли: httpd.conf, srm.conf, access.conf. У останніх версіях Apache ці три файли було об’єднано в один — httpd.conf. Ми вже розглядали основні моменти цього конфігураційного файлу. У наведених нижче прикладах для тестування використовувалася версія Apache з файлом EasyPhp, а отже, і її конфігураційний файл. У ньому DocumentRoot, що позначає корінь дерева веб-сторінок, — це e:\program files\easyphp\www.

8.1.8.2. Посилання на PHP — Apache

Для тестування створіть файл intro.php, що містить лише такий рядок:

<? phpinfo() ?>

і розмістіть його в кореневій папці сервера Apache (DocumentRoot вище). Відкрийте сторінку URL за адресою http://localhost/intro.php. Повинно з’явитися таке інформаційне вікно:

Image

Наступний скрипт PHP відображає час. Ми вже з ним стикалися:

<?php
   // time: кількість мілісекунд з 01.01.1970
   // «формат відображення дати та часу»
   // d: день (2 цифри)
   // m: місяць (2 цифри)
   // y: рік у 2 цифри
   // H: година 0,23
   // i: хвилини
   // s: секунди
  print "Nous sommes le " . date("d/m/y H:i:s",time());
?>

Помістимо цей текстовий файл у кореневу папку сервера Apache (DocumentRoot) і назвемо його date.php. Відкриємо у браузері сторінку URL http://localhost/date.php. Ми отримаємо таку сторінку:

Image

8.1.8.3. Посилання PERL-APACHE

Це реалізовано за допомогою рядка у вигляді: ScriptAlias /cgi-bin/ "E:/Program Files/EasyPHP/cgi-bin/" у файлі <apache>\conf\httpd.conf. Його синтаксис такий: ScriptAlias /cgi-bin/ "<cgi-bin>", де <cgi-bin> — це папка, у яку можна розміщувати скрипти CGI. CGI (Common Gateway Interface) — це стандарт взаємодії між сервером WEB та додатками. Клієнт запитує у веб-сервера динамічну сторінку, c.a.d, тобто сторінку, згенеровану програмою. Отже, сервер WEB повинен звернутися до програми з проханням згенерувати цю сторінку. CGI визначає взаємодію між сервером і програмою, зокрема спосіб передачі інформації між цими двома об’єктами.

За потреби змініть рядок ScriptAlias /cgi-bin/ на «<cgi-bin>» і перезапустіть сервер Apache. Потім виконайте таке тестування:

  1. Напишіть скрипт:
#!c:\perl\bin\perl.exe

# скрипт PERL, що відображає час

# модулі
use strict;

# програма
my ($secondes,$minutes,$heure)=localtime(time);
print <<FINHTML
Content-Type: text/html

<html>
 <head>
  <title>heure</title>
 </head>
 <body>
  <h1>Il est $heure:$minutes:$secondes</h1>
 </body>
FINHTML
;
  1. Помістіть цей скрипт у <cgi-bin>\heure.pl, де <cgi-bin> — це папка, яка може містити скрипти CGI (див. httpd.conf). Перший рядок #!c:\perl\bin\perl.exe вказує шлях до виконуваного файлу perl.exe. За необхідності його слід змінити.
  2. Запустіть Apache, якщо це ще не зроблено
  3. Запросіть у браузері файл URL http://localhost/cgi-bin/heure.pl. Ви побачите таку сторінку:

Image

8.1.9. Сервер PWS

8.1.9.1. Installation

Сервер PWS (Personal Web Server) — це персональна версія сервера IIS (Internet Information Server) від Microsoft. Останній доступний на комп’ютерах з ОС NT та 2000. На комп’ютерах з ОС Win9x PWS зазвичай входить до складу інсталяційного пакета Internet Explorer. Однак він не встановлюється за замовчуванням. Необхідно виконати індивідуальну інсталяцію IE і вказати, щоб було встановлено PWS. Крім того, він доступний у пакеті NT 4.0 Option Pack для Windows 95.

8.1.9.2. Перші тести

Кореневий каталог веб-сторінок сервера PWS — це «диск:\inetpub\wwwroot», де «диск» — це диск, на якому ви встановили PWS. Далі ми припускаємо, що це диск D. Отже, URL-адреса http://machine/rep1/page1.html відповідатиме файлу d:\inetpub\wwwroot\rep1\page1.html. Сервер PWS інтерпретує будь-який файл із розширенням .asp (Active Server Pages) як скрипт, який він повинен виконати для створення сторінки HTML.

PWS за замовчуванням працює на порту 80. Веб-сервер Apache також... Тому, якщо у вас є обидва сервери, для роботи з PWS потрібно зупинити Apache. Інший варіант — налаштувати Apache так, щоб він працював на іншому порту. Для цього у файлі конфігурації Apache httpd.conf замінюємо рядок Port 80 на Port 81. Відтепер Apache працюватиме на порту 81 і його можна буде використовувати одночасно з PWS. Якщо PWS вже запущено, а ми вводимо адресу http://localhost, то отримаємо сторінку, схожу на таку:

Image

8.1.9.3. Посилання PHP — PWS

  1. Нижче наведено файл .reg, призначений для внесення змін до реєстру. Двічі клацніть на цьому файлі, щоб внести зміни до реєстру. Тут необхідний dll знаходиться в d:\php4 разом із виконуваним файлом php. Внесіть зміни за потреби. У шляху до DLL символи \ слід подвоїти.
REGEDIT4

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\w3svc\parameters\Script Map]
".php"="d:\\php4\\php4isapi.dll"
  1. Перезапустіть комп'ютер, щоб зміни в реєстрі набули чинності.
  1. Створіть папку php у d:\inetpub\wwwroot, яка є кореневою папкою сервера PWS. Після цього запустіть PWS і перейдіть на вкладку «Додатково». Натисніть кнопку «Додати», щоб створити віртуальну папку:
Répertoire/Parcourir : d:\inetpub\wwwroot\php
Alias : php
Cocher la case exécuter.
  1. Підтвердіть всі зміни та перезапустіть PWS. Вставте у d:\inetpub\wwwroot\php файл intro.php, що містить лише такий рядок:
<? phpinfo() ?>
  1. Запитайте у сервера PWS файл URL http://localhost/php/intro.php. Ви маєте побачити список інформації PHP, яка вже відображається за допомогою Apache.

8.1.10. Tomcat: сервлети Java та сторінки JSP (Java Server Pages)

Tomcat — це веб-сервер, що дозволяє генерувати сторінки HTML за допомогою сервлетів (Java-програм, що виконуються веб-сервером) або сторінок JSP (Java Server Pages) — сторінок, що поєднують Java-код та HTML-код. Це еквівалент сторінок ASP (Active Server Pages) сервера IIS/PWS від Microsoft, де поєднуються код VBScript або JavaScript із кодом HTML.

8.1.10.1. Installation

Tomcat доступний за адресою URL: http://jakarta.apache.org. Завантажується інсталяційний файл .exe. При запуску ця програма спочатку вказує, яку версію Java вона буде використовувати. Справа в тому, що Tomcat потребує Java для інсталяції, а також для подальшої компіляції та виконання Java-сервлетів. Тому перед встановленням Tomcat необхідно встановити JDK Java. Рекомендується використовувати найновішу версію JD. У процесі встановлення буде створено каталог <tomcat>:

Image

Встановлення полягає просто в розпакуванні цього архіву в каталог. Виберіть каталог, шлях до якого містить лише імена без пробілів (наприклад, не «Program Files»), оскільки в процесі встановлення Tomcat є помилка. Наприклад, C:\tomcat або D:\tomcat. Назвемо цей каталог <tomcat>. У ньому буде папка з назвою jakarta-tomcat, а в ній — така структура каталогів:

LOGS           <REP>        15/11/00   9:04 logs
LICENSE              2 876  18/04/00  15:56 LICENSE
CONF           <REP>        15/11/00   8:53 conf
DOC            <REP>        15/11/00   8:53 doc
LIB            <REP>        15/11/00   8:53 lib
SRC            <REP>        15/11/00   8:53 src
WEBAPPS        <REP>        15/11/00   8:53 webapps
BIN            <REP>        15/11/00   8:53 bin
WORK           <REP>        15/11/00   9:04 work

8.1.10.2. Запуск/зупинка веб-сервера Tomcat

Tomcat — це веб-сервер, подібний до Apache або PWS. Щоб запустити його, скористайтеся посиланнями в меню програм:

Start Tomcat
для запуску Tomcat
Stop Tomcat
для його зупинки

Під час запуску Tomcat з’являється вікно командного рядка з таким вмістом:

Image

Це вікно командного рядка можна згорнути в іконку. Воно залишатиметься активним, доки Tomcat працюватиме. Після цього можна перейти до перших тестів. Веб-сервер Tomcat працює на порту 8080. Після запуску Tomcat відкрийте веб-браузер і введіть адресу URL http://localhost:8080. Ви повинні побачити таку сторінку:

Image

Перейдіть за посиланням «Servlet Examples»:

Image

Натисніть на посилання Execute з RequestParameters, а потім на посилання з Source. Ви отримаєте перше уявлення про те, що таке Java-сервлет. Те саме можна зробити з посиланнями на сторінках JSP.

Щоб зупинити Tomcat, скористайтеся посиланням «Stop Tomcat» у меню програм.

8.1.11. JBuilder

Jbuilder — це середовище розробки Java-додатків. Для створення Java-сервлетів без графічного інтерфейсу наявність такого середовища не є обов’язковою. Достатньо текстового редактора та JDK. Однак JBuilder надає кілька додаткових переваг порівняно з попереднім методом:

  • зручність налагодження: компілятор позначає помилкові рядки програми, і до них легко перейти
  • підказки щодо коду: під час використання об’єкта Java JBuilder відображає в рядку список його властивостей та методів. Це дуже зручно, враховуючи, що більшість об’єктів Java мають дуже багато властивостей і методів, які важко запам’ятати.

JBuilder можна знайти на сайті http://www.borland.com/jbuilder. Щоб отримати програмне забезпечення, потрібно заповнити форму. Ключ активації надсилається електронною поштою. Наприклад, для встановлення JBuilder 7 було зроблено наступне:

  • було завантажено три zip-файли: для програми, для документації та для прикладів. Кожен із цих zip-файлів має окреме посилання на сайті JBuilder.
  • Спочатку було встановлено програму, потім документацію, а наостанок — приклади
  • під час першого запуску програми вимагається ввести ключ активації: це той ключ, який вам надіслали електронною поштою. У версії 7 цей ключ фактично є повним текстовим файлом, який можна, наприклад, розмістити в папці інсталяції JB7. У момент запиту ключа потрібно вказати цей файл. Після цього ключ більше не запитуватиметься.

Якщо ви хочете використовувати JBuilder для створення Java-сервлетів, потрібно виконати кілька корисних налаштувань. Справа в тому, що так звана «особиста версія JBuilder» є спрощеною версією, яка, зокрема, не містить усіх класів, необхідних для веб-розробки на Java. Можна налаштувати JBuilder так, щоб він використовував бібліотеки класів, що надаються Tomcat. Для цього потрібно:

  • запустити JBuilder

Image

  • увімкніть опцію «Tools/Configure JDKs»

Image

У розділі «JDK Settings», показаному вище, у полі «Name» зазвичай вказано «JDK 1.3.1». Якщо у вас є новіша версія JDK, скористайтеся кнопкою «Change», щоб вказати каталог, у якому вона встановлена. Вище вказано каталог E:\Program Files\jdk14, де було встановлено JDK 1.4. Відтепер JBuilder використовуватиме цей JDK для компіляції та виконання. У розділі (Class, Source, Documentation) міститься список усіх бібліотек класів, які будуть аналізуватися JBuilder, у даному випадку — класи з JDK 1.4. Класів з цієї версії недостатньо для веб-розробки на Java. Щоб додати інші бібліотеки класів, використовуємо кнопку «Add» і вказуємо додаткові файли .jar, які хочемо використовувати. Файли .jar — це бібліотеки класів. Tomcat 4.x містить усі бібліотеки класів, необхідні для веб-розробки. Вони знаходяться в <tomcat>\common\lib, де <tomcat> — це каталог інсталяції Tomcat:

Image

За допомогою кнопки «Add» ми додамо ці бібліотеки, одну за одною, до списку бібліотек, які сканує JBuilder:

Image

Відтепер можна компілювати Java-програми, що відповідають стандарту J2EE, зокрема Java-сервлети. JBuilder використовується лише для компіляції, а виконання згодом забезпечується Tomcat відповідно до інструкцій, викладених у курсі.

8.2. Вихідний код програм

8.2.1. Універсальний клієнт TCP

Багато сервісів, створених на початку розвитку Інтернету, працюють за моделлю сервера-відлуння, який ми розглядали раніше: обмін даними між клієнтом і сервером відбувається шляхом обміну рядками тексту. Ми напишемо універсальний TCP-клієнт, який запускатиметься таким чином: java cltTCPgenerique сервер порт

Цей клієнт TCP підключиться до порту port сервера serveur. Після цього він створить два потоки:

  1. потік, відповідальний за зчитування команд, введених з клавіатури, та їх надсилання на сервер
  2. потік, відповідальний за зчитування відповідей сервера та їх відображення на екрані

Чому два потоки? Кожна служба TCP-IP має свій особливий протокол, і іноді трапляються такі ситуації:

  • клієнт повинен надіслати кілька рядків тексту, перш ніж отримати відповідь
  • відповідь сервера може містити кілька рядків тексту

Тому цикл «відправлення одного рядка на сервер — отримання одного рядка від сервера» не завжди підходить. Тому ми створимо два окремі цикли:

  • цикл зчитування команд, введених з клавіатури для відправки на сервер. Користувач позначатиме кінець команд ключовим словом fin.
  • цикл прийому та відображення відповідей від сервера. Це буде нескінченний цикл, який перериватиметься лише у разі закриття мережевого потоку сервером або користувачем за допомогою клавіатури, який введе команду fin.

Щоб ці два цикли працювали незалежно один від одного, нам потрібні два незалежні потоки. Розглянемо приклад виконання, у якому наш загальний TCP-клієнт підключається до сервісу SMTP (SendMail Transfer Protocol). Цей сервіс відповідає за доставку електронної пошти адресатам. Він працює на порту 25 і використовує протокол взаємодії типу обміну текстовими рядками.

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. Після цього сервер закриває мережеве з’єднання. Потік читання може виявити цю подію та зупинитися.
  • Потім користувач вводить «fin» на клавіатурі, щоб також зупинити потік читання команд, введених з клавіатури.

Якщо перевірити отриману пошту, ми побачимо таке (Outlook):

Image

Слід зауважити, що служба SMTP не може визначити, чи є відправник дійсним. Тому ніколи не можна довіряти полю from у повідомленні. У цьому випадку відправник machin@univ-angers.fr не існував.

Цей загальний TCP-клієнт дозволяє нам виявити протокол взаємодії інтернет-сервісів і на цій основі створити спеціалізовані класи для клієнтів цих сервісів. Давайте визначимо протокол взаємодії сервісу POP (Post Office Protocol), який дозволяє отримувати електронні листи, що зберігаються на сервері. Він працює на порту 110.

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]

Основні команди такі:

  • user login, де вказується логін на сервері, на якому зберігаються наші електронні листи
  • pass password, де вказується пароль, пов'язаний із попереднім логіном
  • list — для отримання списку повідомлень у форматі «номер, розмір у байтах»
  • retr i — для читання повідомлення № i
  • quit — для завершення діалогу.

Тепер розглянемо протокол взаємодії між клієнтом і веб-сервером, який зазвичай працює на порту 80:

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 версії 1.0. Найновіша версія цього протоколу — 1.1. Приклад показує, що сервер відповів, надіславши вміст файлу 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 Transfer Protocol). Ми не будемо тут детально розглядати ці заголовки, але нагадаємо, що наш універсальний клієнт надає до них доступ, що може бути корисним для їх розуміння. Наприклад, перший рядок:

<-- HTTP/1.1 200 OK

вказує, що веб-сервер, до якого звернулися, підтримує протокол HTTP/1.1 і що він успішно знайшов запитуваний файл (200 OK), де 200 — це код відповіді HTTP. Рядки

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

повідомляють клієнту, що він отримає 11251 байт, які представляють текст HTML (HyperText Markup Language), і що після завершення передачі з’єднання буде закрито.

Отже, ми маємо дуже зручний TCP-клієнт. Він, безперечно, має менше можливостей, ніж офіційна програма telnet, але було цікаво написати його самостійно. Програма загального TCP-клієнта виглядає так:

// імпортовані пакети
import java.io.*;
import java.net.*;

public class clientTCPgenerique{

    // отримує як параметр характеристики служби у вигляді
     // сервер, порт
     // підключається до служби
     // створює потік для зчитування команд, введених з клавіатури
     // ці команди будуть надіслані на сервер
     // створює потік для зчитування відповідей від сервера
     // вони будуть відображатися на екрані
     // усе завершується командою «fin», введеною з клавіатури

   // змінна екземпляру
  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];

         // порт повинен бути цілим числом >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;
    }// main

     // виведення повідомлень про помилки
    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);
                 // наступна команда
            }//while
        }catch(Exception ex){
             // помилка
            System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // кінець — закриваємо потоки
        try{
            OUT.close();client.close();
        }catch(Exception ex){}
         // повідомляємо про завершення потоку
        System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
    }//run
}//клас

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);
            }//while
        }catch(Exception ex){
            // помилка
            System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // кінець — закриваємо потоки
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // повідомляємо про завершення потоку
        System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
    }//виконання
}//клас

8.2.2. Загальний TCP-сервер

Тепер ми розглянемо сервер,

  • , який виводить на екран команди, надіслані його клієнтами
  • та надсилає їм у відповідь рядки тексту, введені користувачем з клавіатури. Отже, саме користувач виконує роль сервера.

Програму запускають за допомогою команди: java serveurTCPgenerique portEcoute, де portEcoute — це порт, до якого повинні підключатися клієнти. Обслуговування клієнтів забезпечуватимуть два потоки:

  • потік, який присвячений виключно зчитуванню рядків тексту, надісланих клієнтом
  • потік, який виключно займається читанням відповідей, введених користувачем з клавіатури. Цей потік за допомогою команди fin сигналізує про завершення з’єднання з клієнтом.

Сервер створює два потоки на кожного клієнта. Якщо є n клієнтів, одночасно буде активних 2n потоків. Сам сервер ніколи не зупиняється, за винятком випадків, коли користувач натискає комбінацію клавіш Ctrl-C. Розглянемо кілька прикладів.

Сервер запущено на порту 100, і для зв’язку з ним використовується загальний клієнт. Вікно клієнта виглядає так:

E:\data\serge\MSNET\c#\мережа\клієнт tcp загальний> 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. Вищезазначений сервер все ще активний, хоча клієнт 1 завершив роботу. Запускаємо другого клієнта для того самого сервера:


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

    // приймає запити клієнтів на порту прослуховування
     // створює потік для зчитування запитів клієнта
     // вони будуть відображатися на екрані
     // створює потік для зчитування команд, введених з клавіатури
     // вони будуть надіслані клієнту у відповідь
     // усе завершується командою «fin», введеною з клавіатури

    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
    }// кінець main

     // виведення помилок
    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);
                 // наступна відповідь
            }//while
        }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+ "]");
    }//run
}//клас

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);
            }//while
        }catch(Exception ex){
            // помилка
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // кінець  закриваємо потоки
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // повідомляємо про завершення потоку
        System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
    }//run
}//клас

8.3. JAVASCRIPT

У цьому розділі ми наводимо три приклади використання JavaScript на сторінках WEB. Ми зосереджуємося на обробці форм, але JavaScript може зробити набагато більше.

8.3.1. Отримання даних із форми

Наведений нижче приклад демонструє, як отримати в браузері дані, введені користувачем у форму. Зазвичай це дозволяє виконати попередню обробку даних перед їх надсиланням на сервер.

8.3.1.1. Форма

Ми маємо форму, що містить найпоширеніші компоненти, та кнопку «Показати», яка дозволяє відобразити дані, введені користувачем.

Image

8.3.1.2. Код

<html>

  <head>
    <title>Un formulaire traité par Javascript</title>
    <script language="javascript">
      function afficher(){
         // відображає інформацію з форми у списку

         // спочатку очищаємо
        effacerInfos();

         // відображається значення # полів
        with(document.frmExemple){
           // приховане поле
          ecrire("champ caché="+cache.value);
           // просте текстове поле
          ecrire("champ textuel simple="+simple.value);
           // поле з можливістю введення декількох рядків
          ecrire("champ textuel multiple="+lignes.value);
          // перемикачі
          for(i=0;i<radio.length;i++){
            texte="radio["+i+"]="+radio[i].value;
            if(radio[i].checked) texte+=", coché";
            ecrire(texte);
          }//for
             // прапорці
          for(i=0;i<qcm.length;i++){
            texte="qcm["+i+"]="+qcm[i].value;
            if(qcm[i].checked) texte+=", coché";
            ecrire(texte);
          }//for
           // випадаючий список
          ecrire("index sélectionné dans le menu="+menu.selectedIndex);
          for(i=0;i<menu.length;i++){
            texte="menu["+i+"]="+menu.options[i].text;
            if(menu.options[i].selected) texte+=",sélectionné";
            ecrire(texte);
          }//для
           //список з кількома варіантами вибору
          for(i=0;i<lstVoitures.length;i++){
            texte="lstVoitures["+i+"]="+lstVoitures.options[i].text;
            if(lstVoitures.options[i].selected) texte+=",sélectionné";
            ecrire(texte);
          }//для
           //пароль
          ecrire("mot de passe="+passwd.value);
        }//з
      }//функція

      function ecrire(texte){
         // записує текст у список інформації
        frmInfos.lstInfos.options[frmInfos.lstInfos.length]=new Option(texte);
      }//записувати

      function effacerInfos(){
        frmInfos.lstInfos.length=0;
      }//effacerInfos
    </script>
  </head>

  <body bgcolor="#C0C0C0" onload="afficher()">
    <center>
     <h2>Un formulaire traité par Javascript</h2>
    <hr>
    <form method="POST" name="frmExemple">
        <input type="hidden" name="cache" value="secret">
        <table border="0">
        <tr>
            <td align="center">Un champ textuel simple</td>
            <td align="center" width="100">&nbsp;</td>
            <td align="center">Un champ textuel sur plusieurs lignes</td>
        </tr>
        <tr>
            <td align="center"><input type="text" size="20" name="simple"></td>
            <td align="center" width="100">&nbsp;</td>
            <td align="center">
              <textarea name="lignes" rows="2" cols="40">Ce texte est modifiable</textarea>
            </td>
        </tr>
    </table>
    <table border="0">
      <tr>
       <td><strong>Des boutons radio :</strong></td>
        <td>
          <input type="radio" checked name="radio" value="FM">FM
        </td>
        <td>
          <input type="radio" name="radio" value="GO">GO
        </td>
        <td>
          <input type="radio" name="radio" value="PO">PO
        </td>
        <td>&nbsp;</td>
        <td><strong>Des choix multiples :</strong></td>
        <td>
          <input type="checkbox" name="qcm" value="un">un
        </td>
        <td>
          <input type="checkbox" name="qcm" value="deux">deux
        </td>
        <td>
          <input type="checkbox" name="qcm" value="trois">trois
        </td>
     </tr>
    </table>
    <table border="0">
      <tr>
       <td>Un menu déroulant : </td>
        <td>
          <select name="menu" size="1">
             <option>50 F</option>
              <option>60 F</option>
              <option>70 F</option>
              <option>100 F</option>
            </select>
        </td>
          <td>Une liste :</td>
           <td>
             <select name="lstVoitures" multiple size="3">
                <option>Renault</option>
                <option>Citroën</option>
                <option>Peugeot</option>
                <option>Fiat</option>
                <option>Audi</option>
              </select>
           </td>
        </tr>
    </table>
    <table border="0">
        <tr>
            <td>Un mot de passe : </td>
            <td><input type="password" size="21" name="passwd"></td>
            <td>&nbsp;</td>
            <td>Un champ de contexte caché : </td>
        </tr>
    </table>
    </form>
    <hr>
    <h2>Informations du formulaire</h2>
    <form name="frmInfos">
      <table>
        <tr>
          <td><input type="button" value="Effacer" onclick="effacerInfos()"></td>
          <td>
            <select name="lstInfos" multiple size="3">
            </select>
          </td>
          <td>
            <input type="button" name="cmdAfficher" value="Afficher" onclick="afficher()">
          </td>
        </tr>
    </form>
  </body>
</html>

8.3.2. Регулярні вирази в JavaScript

У браузері JavaScript можна використовувати для перевірки правильності даних, введених користувачем, перед їх надсиланням на сервер. Ось програма для тестування цих регулярних виразів.

8.3.2.1. Сторінка для тестування

Image

8.3.2.2. Код сторінки

<html>

  <head>
    <title>Les expressions régulières en Javascript</title>
    <script language="javascript">
      function afficherInfos(){
        with(document.frmRegExp){
           // щось потрібно зробити?
          if (! verifier()) return;
          // все гаразд — видаляємо попередні результати
          effacerInfos();
           // перевірка шаблону
          modele=new RegExp(txtModele.value);
          champs=modele.exec(txtChaine.value);
          if(champs==null)
             // немає збігу між шаблоном і рядком
            ecrireInfos("pas de correspondance");
          else{
             // збіг — відображаємо отримані результати
            ecrireInfos("Il y a correspondance");
            for(i=0;i<champs.length;i++)
              ecrireInfos("champs["+i+"]=["+champs[i]+"]");
          }//інакше
        }//з
      }//функція

      function ecrireInfos(texte){
        // записує текст у список інформації
        document.frmRegExp.lstInfos.options[document.frmRegExp.lstInfos.length]=new Option(texte);
      }//запис

      function effacerInfos(){
        frmRegExp.lstInfos.length=0;
      }//effacerInfos

      function jouer(){
         // перевіряє відповідність шаблону рядку у вибраному прикладі
        with(document.frmRegExp){
          txtModele.value=lstModeles.options[lstModeles.selectedIndex].text
          txtChaine.value=lstChaines.options[lstChaines.selectedIndex].text
          afficherInfos();
        }//з
      }//відтворити

      function ajouter(){
         //додає поточний тест до прикладів
        with(document.frmRegExp){
          // що робити?
          if (! verifier()) return;
           // додати
          lstModeles.options[lstModeles.length]=new Option(txtModele.value);
          lstChaines.options[lstChaines.length]=new Option(txtChaine.value);
           // очистити введені дані
          txtModele.value="";
          txtChaine.value="";
        }//з
      }//додати

      function verifier(){
         // перевірити, чи поля введення не порожні
        with(document.frmRegExp){
          champs=/^\s*$/.exec(txtModele.value);
          if(champs!=null){
            alert("Vous n'avez pas indiqué de modèle");
            txtModele.focus();
            return false;
          }//якщо
          champs=/^\s*$/.exec(txtChaine.value);
          if(champs!=null){
            alert("Vous n'avez pas indiqué de chaîne de test");
            txtChaine.focus();
            return false;
          }//якщо
           // все гаразд
          return true;
        }//з
      }//перевірити
    </script>
  </head>

  <body bgcolor="#C0C0C0">
    <center>
     <h2>Les expressions régulières en Javascript</h2>
    <hr>
    <form name="frmRegExp">
      <table>
        <tr>
          <td>Expression régulière</td>
          <td>Chaîne de test</td>
        </tr>
        <tr>
          <td><input type="text" name="txtModele" size="20"></td>
          <td><input type="text" name="txtChaine" size="20"></td>
        </tr>
        <tr>
          <td>
            <input type="button" name="cmdAfficher" value="Jouer le test" onclick="afficherInfos()">
          </td>
          <td>
            <input type="button" name="cmdAjouter" value="Ajouter aux exemples" onclick="ajouter()">
          </td>
        </tr>
      </table>
      <hr>
      <h2>Résultats de l'instruction champs=expression régulière.exec(chaine)</h2>
      <table>
        <tr>
          <td>
            <select name="lstInfos" size="3">
            </select>
          </td>
        </tr>
      </table>
      <hr>
      <h2>Exemples</h2>
      <table>
 <tr>
          <td align="center">Modèles</td>
          <td align="center">Chaînes</td>
        </tr>
        <tr>
          <td>
            <select name="lstModeles" size="1">
              <option>^\d+$</option>
              <option>^(\d+) (\d+)$</option>
              <option>^(\d+)(.*)(\d+)$</option>
              <option>^(\d+)(\s+)(\d+)$</option>
            </select>
          </td>
          <td>
            <select name="lstChaines" size="1">
              <option>67</option>
              <option>56 84</option>
              <option>45abcd67</option>
              <option>45   67</option>
            </select>
          </td>
          <td>
            <input type="button" name="cmdJouer" value="Jouer l'exemple" onclick="jouer()">
          </td>
        </tr>
    </form>
  </body>
</html>

8.3.3. Управління списками в JavaScript

8.3.3.1. Форма

Image

8.3.3.2. Код

<html>

  <head>
    <title>Les listes en Javascript</title>

    <script language="javascript">
       // додати
      function ajouter(L1,L2,T){
         // додає значення поля T до списків L1, L2
           // що робити?
          champs=/^\s*$/.exec(T.value);
          if(champs!=null){
            // поле порожнє
            alert("Vous n'avez pas indiqué la valeur à ajouter");
            txtElement.focus();
            return;
          }//if
           // додається елемент
          L1.options[L1.length]=new Option(T.value);
          L2.options[L2.length]=new Option(T.value);
          T.value="";
      }//додати

       //очистити
      function vider(L){
         // очищає список L
        L.length=0;
      }//очистити

      //перенести
      function transfert(L1,L2,simple){
        //перенести у L2 елементи, вибрані зі списку L1

         // що робити?
         // індекс вибраного елемента у списку L1
        index1=L1.selectedIndex;
        if(index1==-1){
          alert("Vous n'avez pas sélectionné d'élément");
          return;
        }//if
         // який режим вибору елементів зі списків
        if(simple){ // просте виділення
          element1=L1.options[index1].text;
           //додавання у L2
          L2.options[L2.length]=new Option(element1);
          //видалення в L1
          L1.options[index1]=null;
        }//простий
        if(! simple){ //множинний вибір
           //перегляд списку 1 у зворотному напрямку
          for(i=L1.length-1;i>=0;i--){
            //елемент вибрано?
            if(L1.options[i].selected){
               //додати до L2
              L2.options[L2.length]=new Option(L1.options[i].text);
              //видалити з L1
              L1.options[i]=null;
            }//якщо
          }//для i
        }//if ! simple
      }//переказ
   </script>
  </head>

  <body bgcolor="#C0C0C0">
    <center>
     <h2>Les listes en Javascript</h2>
    <hr>
    <form name="frmListes">
      <table>
        <tr>
          <td>
            <input type="button" name="cmdAjouter" value="Ajouter" onclick="ajouter(lst1A,lst1B,txtElement)">
          </td>
          <td>
            <input type="text" name="txtElement">
          </td>
        </tr>
      </table>
      <table>
        <tr>
          <td align="center">liste 1</td>
          <td align="center"><input type="button" value=">>" onclick="transfert(lst1A,lst2A,true)"</td>
          <td align="center"><input type="button" value="<<" onclick="transfert(lst2A,lst1A,true)"</td>
          <td align="center">liste 2</td>
          <td width="30"></td>
          <td align="center">liste 1</td>
          <td align="center"><input type="button" value=">>" onclick="transfert(lst1B,lst2B,false)"</td>
          <td align="center"><input type="button" value="<<" onclick="transfert(lst2B,lst1B,false)"</td>
          <td align="center">liste 2</td>
        </tr>
         <tr>
          <td></td>
          <td align="center">
            <select name="lst1A" size="5">
            </select>
          </td>
          <td align="center">
            <select name="lst2A" size="5">
            </select>
          </td>
          <td></td>
          <td></td>
          <td></td>
          <td align="center">
            <select name="lst1B" size="5" multiple >
            </select>
          </td>
          <td align="center">
            <select name="lst2B" size="5" multiple>
            </select>
          </td>

        </tr>
        <tr>
          <td></td>
          <td align="center"><input type="button" value="Vider" onclick="vider(lst1A)"</td>
          <td align="center"><input type="button" value="Vider" onclick="vider(lst2A)"</td>
          <td></td>
          <td></td>
          <td></td>
          <td align="center"><input type="button" value="Vider" onclick="vider(lst1B)"</td>
          <td align="center"><input type="button" value="Vider" onclick="vider(lst2B)"</td>
          <td></td>
        </tr>
        <tr>
          <td></td>
          <td colspan="2"><strong>Sélection simple</strong></td>
          <td></td>
          <td></td>
          <td></td>
          <td colspan="2"><strong>Sélection multiple</strong></td>
          <td></td>
        </tr>

      </table>
      <hr>
    </form>
  </body>
</html>