Когда число компьютеров в учреждении переваливает за десяток и сотрудникам надоедает бегать с дискетами друг к другу для обмена файлами, тогда в компьютеры вставляются сетевые карты, протягиваются кабели и компьютеры объединяются в сеть. Сначала все компьютеры в сети равноправны, они делают одно и то же — это одноранговая (peer-to-peer) сеть. Потом покупается компьютер с большими и быстрыми жесткими дисками, и все файлы учреждения начинают храниться на данных дисках — этот компьютер становится файл-сервером, предоставляющим услуги хранения, поиска, архивирования файлов. Затем покупается дорогой и быстрый принтер. Компьютер, связанный с ним, становится принт-сервером, предоставляющим услуги печати. Потом появляются графический сервер, вычислительный сервер, сервер базы данных. Остальные компьютеры становятся клиентами этих серверов. Такая архитектура сети называется архитектурой клиент-сервер (client-server).

Сервер постоянно находится в состоянии ожидания, он прослушивает (listen) сеть, ожидая запросов от клиентов. Клиент связывается с сервером и посылает ему запрос (request) с описанием услуги, например имя нужного файла. Сервер обрабатывает запрос и отправляет ответ (response), в нашем примере — файл, или сообщение о невозможности оказать услугу. После этого связь может быть разорвана или продолжится, организуя сеанс связи, называемый иногда сессией (session).

Запросы клиента и ответы сервера формируются по строгим правилам, совокупность которых образует протокол (protocol) связи. Всякий протокол должен, прежде всего, содержать правила соединения компьютеров. Клиент перед посылкой запроса должен удостовериться, что сервер в рабочем состоянии, прослушивает сеть и услышал клиента. Послав запрос, клиент должен быть уверен, что запрос дошел до сервера, сервер понял запрос и готов ответить на него. Сервер обязан убедиться, что ответ дошел до клиента. Окончание сессии должно быть четко зафиксировано, чтобы сервер мог освободить ресурсы, занятые обработкой запросов клиента.

Все правила, образующие протокол, должны быть понятными, однозначными и короткими, чтобы не загружать сеть. Поэтому сообщения, пересылаемые по сети, напоминают шифровки, в них имеет значение каждый бит.

Итак, все сетевые соединения базируются на трех основных понятиях: клиент, сервер и протокол. Клиент и сервер — понятия относительные. В одной сессии компьютер может быть сервером, а в другой — клиентом. Например, файл-сервер может послать принт-серверу файл на печать, становясь его клиентом.

Для обслуживания протокола: формирования запросов и ответов, проверок их соответствия протоколу, расшифровки сообщений, связи с сетевыми устройствами создается программа, состоящая из двух частей. Одна часть программы работает на сервере, другая — на клиенте. Эти части так и называются — серверной частью программы и клиентской частью программы, или, короче, сервером и клиентом.

Очень часто клиентская и серверная части программы пишутся отдельно, разными фирмами, поскольку от этих программ требуется только, чтобы они соблюдали протокол. Более того, по каждому протоколу работают десятки клиентов и серверов, отличающихся разными удобствами.

Обычно на одном компьютере-сервере работают несколько программ-серверов. Одна программа занимается приемом и отправкой электронной почты, другая — пересылкой файлов, третья предоставляет Web-страницы. Для того чтобы различать программы-серверы, каждой программе-серверу назначается номер порта (port). Это просто целое положительное число, которое указывает клиент, обращаясь к определенной программе-серверу. Число, вообще говоря, может быть любым, но наиболее распространенным протоколам даются стандартные номера, чтобы клиенты были твердо уверены, что обращаются к нужному серверу. Так, стандартный номер порта электронной почты — 25, пересылки файлов — 21, Web-сервера — 80. Стандартные номера простираются до 1023. Числа, начиная с 1024 до 65 535, можно использовать для своих собственных номеров портов.

Все это похоже на телевизионные каналы. Клиент-телевизор обращается посредством антенны к серверу-телецентру и выбирает номер канала. Он уверен, что на первом канале телекомпания "Первый канал", на втором — "РТР" и т. д. Другой пример — поликлиника, в которой больных клиентов обслуживают врачи-"серверы". Врачи сидят в своих кабинетах, различающихся по номерам. Больной, кроме адреса поликлиники, должен еще знать номер кабинета нужного ему врача.

Чтобы равномерно распределить нагрузку на сервер, часто несколько портов прослушиваются несколькими экземплярами программы-сервера одного типа. Web-сервер кроме порта с номером 80 может прослушивать порт 81, 8080, 8001 и еще какой-нибудь другой.

В процессе передачи сообщения используется несколько протоколов. Даже при отправлении обычного письма мы берем лист бумаги и пишем сообщение, начиная его: "Глубокоуважаемый Иван Петрович!" и заканчивая: "Искренне преданный Вам...". Это один протокол. Можно начать письмо словами: "Вася, привет!" и закончить: "Ну, пока". Это другой протокол. Потом мы помещаем письмо в конверт и пишем на нем адрес по протоколу, предложенному Министерством связи. Затем письмо попадает в почтовое отделение, упаковывается в мешок, на котором пишется адрес по протоколу почтовой связи. Мешок загружается в самолет, который перемещается по своему протоколу. Заметьте, что каждый протокол только добавляет к сообщению свою информацию, не меняя его, ничего не зная о том, что сделано по предыдущему протоколу и что будет выполнено по правилам следующего протокола. Это очень удобно — можно программировать один протокол, ничего не зная о других протоколах.

Прежде чем дойти до адресата, письмо проходит обратный путь: сначала вынимается из самолета, затем вынимается из мешка, потом из конверта. Поэтому говорят о стеке (stack) протоколов: "Первым пришел — последним ушел".

В современных глобальных сетях принят стек из четырех протоколов, называемый стеком протоколов TCP/IP.

Сначала мы пишем сообщение, пользуясь программой, реализующей прикладной (application) протокол: HTTP (80), SMTP (25), Telnet (23), fTp (21), POP3 (100) или другой протокол. В скобках записан стандартный номер порта.

Затем сообщение обрабатывается по транспортному (transport) протоколу. К нему добавляются, в частности, номера портов отправителя и получателя, контрольная сумма и длина сообщения. Наиболее распространены транспортные протоколы TCP (Transmission Control Protocol) и UDP (User Datagram Protocol). В результате работы протокола TCP получается TCP-пакет (packet), а протокола UDP — дейтаграмма (datagram).

Дейтаграмма невелика — всего около килобайта, поэтому сообщение делится на прикладном уровне на части, из которых создаются отдельные дейтаграммы. Дейтаграммы посылаются одна за другой. Они могут идти к получателю разными маршрутами, прийти совсем в другом порядке, некоторые дейтаграммы могут потеряться. Прикладная программа получателя должна сама позаботиться о том, чтобы собрать из полученных дейтаграмм исходное сообщение. Для этого обычно перед посылкой части сообщения нумеруются, как страницы в книге. Таким образом, протокол UDP работает как почтовая служба. Посылая книгу, мы разрезаем ее на страницы, каждую страницу отправляем в своем конверте, и никогда не можем быть уверены, что все письма дойдут до адресата.

TCP-пакет тоже невелик, и пересылка также идет отдельными пакетами, но протокол TCP обеспечивает надежную связь. Сначала устанавливается соединение с получателем. Только после этого посылаются пакеты. Получение каждого пакета подтверждается получателем, при ошибке посылка пакета повторяется. Сообщение аккуратно собирается получателем. Для отправителя и получателя создается впечатление, что пересылаются не пакеты, а сплошной поток байтов, поэтому передачу сообщений по протоколу TCP часто называют передачей потоком. Связь по протоколу TCP больше напоминает телефонный разговор, чем почтовую связь.

Далее сообщением занимается программа, реализующая сетевой (network) протокол. Чаще всего это протокол IP (Internet Protocol). Он добавляет к сообщению адрес отправителя и адрес получателя, и другие сведения. В результате получается IP-пакет.

Наконец, IP-пакет поступает к программе, работающей по канальному (link) протоколу ENET, SLIP, PPP, и сообщение принимает вид, пригодный для передачи по сети.

На стороне получателя сообщение проходит через эти четыре уровня протоколов в обратном порядке, освобождаясь от служебной информации, и доходит до программы, реализующей прикладной протокол.

Какой же адрес заносится в IP-пакет? Каждый компьютер или другое устройство, подключенное к объединению сетей Интернета, так называемый хост (host), получает уникальный номер — четырехбайтовое целое число, называемое IP-адресом (IP-address). По традиции содержимое каждого байта записывается десятичным числом от 0 до 255, называемым октетом (octet), и эти числа пишутся через точку, например: 138.2.45.12 или 17.056.215.38.

IP-адрес удобен для машины, но неудобен для человека. Представьте себе рекламный призыв: "Заходите на наш сайт 154.223.145.26!" Поэтому IP-адрес хоста дублируется доменным именем (domain name).

В доменном имени присутствует краткое обозначение страны: ru — Россия, su — Советский Союз, ua — Украина, de — ФРГ и т. д., или обозначение типа учреждения: com — коммерческая структура, org — общественная организация, edu — образовательное учреждение. Далее указывается регион: msc.ru — Москва, spb.ru — Санкт-Петербург, kcn.ru — Казань, или учреждение: bhv.ru — "БХВ-Петербург", ksu.ru — Казанский госуниверситет, sun.com — Sun Microsystems. Потом подразделение: www.bhv.ru,java.sun.com. Такую цепочку кратких обозначений можно продолжать и дальше.

В Java IP-адрес и доменное имя объединяются в один класс InetAddress пакета java.net. Экземпляр этого класса создается статическим методом getByName(String host) данного же класса, в котором аргумент host — это доменное имя или IP-адрес.

Работа в WWW

Среди программного обеспечения Интернета большое распространение получила информационная система WWW (World Wide Web), основанная на прикладном протоколе HTTP (Hypertext Transfer Protocol). В ней используется расширенная адресация, называемая URL (Uniform Resource Locator). Эта адресация имеет такие схемы:

protocol://authority@host:port/path/file#ref

protocol://authority@host:port/path/file/query?user_info

Здесь необязательная часть authority — это пара имя:пароль для доступа к хосту, host — это IP-адрес или доменное имя хоста. Например:

ftp://guest:[email protected]/users/local/pub

Если какой-то из элементов адреса URL отсутствует, то берется его стандартное значение. Например, номер порта протокола HTTP по умолчанию равен 80, протокола HTTPS — 443, а протокола FTP — 21. Если отсутствует путь к имени файла path/file, то сервер отправляет клиенту файл, определяемый хостом, так называемый "welcome file". Чаще всего это файл с именем index.html, index.htm или indexjsp.

В Java для работы с URL есть класс url пакета j ava. net. Объект этого класса создается одним из шести конструкторов. В основном конструкторе

URL(String url) ;

задается расширенный адрес url в виде строки. Кроме методов доступа getProtocol(),

getAuthority(), getHost(), getPort(), getPath(), getFile(), getQuery(), getUserInfo(), позволяющих получить элементы URL, в этом классе есть два интересных метода:

□ openConnection ( ) -определяет связь с URL и возвращает объект класса URLConnection;

□ openStream () — устанавливает связь с URL и открывает входной поток в виде возвращаемого объекта класса InputStream.

Листинг 24.1 показывает, как легко можно получить файл из Интернета, пользуясь методом openStream().

Листинг 24.1. Получение Web-страницы

import java.net.*; import java.io.*;

public class SimpleURL{

public static void main(String[] args){ try{

URL bhv = new URL("");

BufferedReader br = new BufferedReader( new InputStreamReader(bhv.openStream()));

String line = null;

while ((line = br.readLine()) != null)

System.out.println(line); br.close();

}catch(MalformedURLException me){

System.err.println("Unknown host: " + me);

System.exit(0);

}catch(IOException ioe){

System.err.println("Input error: " + ioe);

}

}

}

Если вам надо не только получить информацию с хоста, но и узнать ее тип: текст, гипертекст, архивный файл, изображение, звук, или выяснить длину файла, или передать информацию на хост, то необходимо сначала методом openConnection() создать объект класса URLConnection или его подкласса HttpURLConnection.

После создания объекта соединение еще не установлено, и можно задать параметры связи. Это делается следующими методами:

□ setDoOutput(boolean out) — если аргумент out равен true, то передача пойдет от клиента на хост; значение по умолчанию false;

□ setDoInput(boolean in) — если аргумент in равен true, то передача пойдет с хоста к клиенту; значение по умолчанию true, но если уже выполнено setDoOutput (true), то значение по умолчанию равно false;

□ setUseCaches (boolean cache) — если аргумент cache равен false, то передача пойдет без кэширования, если true, то принимается режим по умолчанию;

□ setDefaultUseCaches(boolean default) — если аргумент default равен true, то принимается режим кэширования, предусмотренный протоколом;

□ setRequestProperty(String name, String value) — добавляет параметр name со значением value к заголовку посылаемого сообщения.

После задания параметров нужно установить соединение методом connect (). После соединения задание параметров связи уже невозможно. Следует учесть, что некоторые методы доступа getXxx (), которым надо получить свои значения с хоста, автоматически устанавливают соединение, и обращение к методу connect () становится излишним.

Web-сервер возвращает информацию, запрошенную клиентом, вместе с заголовком, сведения из которого можно получить многочисленными методами getXxx (). Вот некоторые из них:

□ getContentType () — возвращает строку типа String, показывающую MIME-тип пересланной информации, например "text/html", или null, если сервер его не указал;

□ getContentLength () — возвращает длину полученной информации в байтах или —1, если сервер ее не указал;

□ getContent () — возвращает полученную информацию в виде объекта типа Obj ect;

□ getContentEncoding ( ) - возвращает строку типа String с кодировкой полученной ин

формации или null, если сервер ее не указал.

Два метода возвращают потоки ввода/вывода, созданные для данного соединения:

□ getInputStream ( ) -возвращает входной поток типа InputStream;

□ getOutputStream ( ) -возвращает выходной поток типа OutputStream.

Прочие методы, а их около двадцати, возвращают различные параметры соединения.

Обращение к методу bhv.openStream(), записанное в листинге 24.1, — это на самом деле сокращение записи

bhv.openConnection().getInputStream();

В листинге 24.2 показано, как переслать строку текста по адресу URL.

Web-сервер, который получает эту строку, не знает, что делать с полученной информацией. Занести ее в файл? Но с каким именем, и есть ли у него право создавать файлы? Переслать на другую машину? Но куда?

Выход был найден в системе CGI (Common Gateway Interface), которая, вкратце, действует следующим образом. При посылке сообщения мы указываем URL исполнимого файла некоторой программы, размещенной на машине-сервере. Получив сообщение, Web-сервер отыскивает и запускает эту программу и передает сообщение на ее стандартный ввод. Вот программа-то и знает, что делать с полученным сообщением. Она обрабатывает сообщение и выводит результат обработки на свой стандартный вывод. Web-сервер подключается к стандартному выводу программы, принимает результат ее работы и отправляет его обратно клиенту.

CGI-программу можно написать на любом языке: C, C++, Pascal, Perl, ASP, PHP, лишь бы у нее был стандартный ввод и стандартный вывод. Можно написать ее и на Java, но в технологии Java есть более изящное решение этой задачи с помощью сервлетов (servlets) и страниц JSP, которые мы рассмотрим в главах 26 и 27. CGI-программы обычно лежат на сервере в каталоге cgi-bin.

Листинг 24.2. Посылка строки по адресу URL

import java.net.*; import java.io.*;

public class PostURL{

public static void main(String[] args){

String req = "This text is posting to URL";

try{

// Указываем URL нужной CGI-программы.

URL url = new URL("");

// Создаем объект uc.

URLConnection uc = url.openConnection();

// Собираемся отправлять uc.setDoOutput(true);

// и получать сообщения uc.setDoInput(true);

// без кэширования. uc.setUseCaches(false);

// Задаем тип

uc.setRequestProperty("content-type", "application/octet-stream");

// и длину сообщения.

uc.setRequestProperty("content-length", "" + req.length());

// Устанавливаем соединение. uc.connect();

// Открываем выходной поток

DataOutputStream dos = new DataOutputStream(uc.getOutputStream());

// и выводим в него сообщение, посылая его на адрес URL. dos.writeBytes(req);

// Закрываем выходной поток. dos.close();

// Открываем входной поток для ответа сервера.

BufferedReader br = new BufferedReader(new InputStreamReader(

uc.getInputStream()));

String res = null;

// Читаем ответ сервера и выводим его на консоль. while ((res = br.readLine()) != null)

System.out.println(res); br.close();

}catch(MalformedURLException me){

System.err.println(me);

}catch(UnknownHostException he){

System.err.println(he);

}catch(UnknownServiceException se){

System.err.println(se);

}catch(IOException ioe){

System.err.println(ioe) ;

}

}

}

Упражнения

1. Напишите программу, получающую файлы с известных вам сайтов Интернета.

2. Напишите программу, связывающуюся с известными вам CGI-программами Интернета и передающую им необходимую информацию.

Работа по протоколу TCP

Программы-серверы, прослушивающие свои порты, работают под управлением операционной системы. У машин-серверов могут быть самые разные операционные системы, особенности которых передаются программам-серверам.

Чтобы сгладить различия в реализациях разных серверов, между сервером и портом введен промежуточный программный слой, названный сокетом (socket). Английское слово "socket" переводится как электрический разъем, розетка. Так же как к электрической розетке при помощи вилки можно подключить любой электрический прибор, лишь бы он был рассчитан на 220 В и 50 Гц, к сокету можно присоединить любой клиент, лишь бы он работал по тому же протоколу, что и сервер. Каждый сокет связан (bind) с одним портом; говорят, что сокет прослушивает (listen) порт. Соединение с помощью сокетов устанавливается так.

1. Сервер создает сокет, прослушивающий порт сервера.

2. Клиент тоже создает сокет, через который связывается с сервером, сервер начинает устанавливать (accept) связь с клиентом.

3. Устанавливая связь, сервер создает новый сокет, прослушивающий порт с другим, новым номером, и сообщает этот номер клиенту.

4. Клиент посылает запрос на сервер через порт с новым номером.

После этого соединение становится совершенно симметричным — два сокета обмениваются информацией, а сервер через старый сокет продолжает прослушивать прежний порт, ожидая следующего клиента.

В Java сокет — это объект класса Socket из пакета j ava. net. В классе семь конструкторов, в которые разными способами заносится адрес хоста и номер порта. Чаще всего применяется конструктор

Socket(String host, int port);

Многочисленные методы доступа устанавливают и получают параметры сокета. Мы не будем углубляться в их изучение. Нам понадобятся только методы, создающие потоки ввода/вывода:

□ getInputStream ( ) -возвращает входной поток типа InputStream;

□ getOutputStream ( ) возвращает выходной поток типа OutputStream.

Приведем пример получения файла с сервера по максимально упрощенному протоколу HTTP. Протокол содержит следующие действия:

1. Клиент посылает серверу запрос на получение файла строкой "POST filename HTTP/1.1\n\n", где filename — строка с путем к файлу на сервере.

2. Сервер анализирует строку, отыскивает файл с именем filename и возвращает его клиенту. Если имя файла filename заканчивается наклонной чертой /, то сервер понимает его как имя каталога и возвращает файл index.html, находящийся в этом каталоге.

3. Перед содержимым файла сервер посылает строку вида "HTTP/1.1 code \n\n", где code — это код ответа, одно из чисел: 200 — запрос удовлетворен, файл посылается; 400 — запрос не понят; 404 — файл не найден.

4. Сервер закрывает сокет и продолжает слушать порт, ожидая следующего запроса.

5. Клиент выводит содержимое полученного файла в стандартный вывод System.out или выводит код сообщения сервера в стандартный вывод сообщений System.err.

6. Клиент закрывает сокет, завершая связь.

Этот протокол реализуется в клиентской программе листинга 24.3 и серверной программе листинга 24.4.

Листинг 24.3. Упрощенный НТТР-клиент

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

public class Client{

public static void main(String[] args){ if (args.length != 3){

System.err.println("Usage: Client host port file");

System.exit(0);

}

String host = args[0];

int port = Integer.parseInt(args[1]);

String file = args[2];

try{

Socket sock = new Socket(host, port);

PrintWriter pw = new PrintWriter(new OutputStreamWriter( sock.getOutputStream()), true);

pw.println("POST " + file + " HTTP/1.1\n");

BufferedReader br = new BufferedReader(new InputStreamReader( sock.getInputStream()));

String line = null; line = br.readLine();

StringTokenizer st = new StringTokenizer(line);

String code = null;

if ((st.countTokens() >= 2) && st.nextToken().equals("POST")){ if ((code = st.nextToken()) != "200"){

System.err.println("File not found, code = " + code); System.exit(0);

}

}

while ((line = br.readLine()) != null)

System.out.println(line);

sock.close();

}catch(Exception e){

System.err.println(e);

}

}

Закрытие потоков ввода/вывода вызывает закрытие сокета. И наоборот, закрытие сокета закрывает и потоки.

Для создания сервера в пакете java.net есть класс ServerSocket. В конструкторе этого класса указывается номер порта:

ServerSocket(int port);

Основной метод этого класса, accept(), ожидает поступления запроса, приостанавливая выполнение серверной программы. Когда запрос получен, метод устанавливает соединение с клиентом и возвращает объект класса Socket, через который сервер будет обмениваться информацией с клиентом.

Листинг 24.4. Упрощенный НТТР-сервер

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

public class Server{

public static void main(String[] args){ try{

ServerSocket ss = new ServerSocket(Integer.parseInt(args[0])); while (true)

new HttpConnect(ss.accept());

}catch(ArrayIndexOutOfBoundsException ae){ System.err.println("Usage: Server port");

System.exit(0);

}catch(IOException e){

System.out.println(e);

}

}

}

class HttpConnect extends Thread{

private Socket sock;

HttpConnect(Socket s){

sock = s;

setPriority(NORM_PRIORITY - 1); start();

}

public void run(){ try{

PrintWriter pw = new PrintWriter(new OutputStreamWriter( sock.getOutputStream()), true);

BufferedReader br = new BufferedReader(new InputStreamReader( sock.getInputStream()));

String req = br.readLine();

System.out.println("Request: " + req);

StringTokenizer st = new StringTokenizer(req); if ((st.countTokens() >= 2) && st.nextToken().equals("POST")){ if ((req = st.nextToken()).endsWith("/") || req.equals("")) req += "index.html"; try{

File f = new File(req);

BufferedReader bfr = new BufferedReader(new FileReader(f)); char[] data = new char[(int)f.length()]; bfr.read(data);

pw.println("HTTP/1.1 200 OK\n"); pw.write(data); pw.flush();

}catch(FileNotFoundException fe){

pw.println("HTTP/1.1 404 Not Found\n");

}catch(IOException ioe){

System.out.println(ioe);

}

}else pw.println("HTTP/1.1 400 Bad Request\n"); sock.close();

}catch(IOException e){

System.out.println(e);

}

}

}

Вначале следует запустить сервер, указав номер порта, например:

java Server 8080

Затем надо запустить клиент, указав IP-адрес или доменное имя хоста, номер порта и имя файла:

java Client localhost 8080 Server.java

Сервер отыскивает файл Server.java в своем текущем каталоге и посылает его клиенту. Клиент выводит содержимое этого класса в стандартный вывод и завершает работу. Сервер продолжает работать, ожидая следующего запроса.

Замечание по отладке

Программы, реализующие стек протоколов TCP/IP, всегда создают так называемую "петлю" с адресом 127.0.0.1 и доменным именем localhost. Это адрес самого компьютера. Он используется для отладки клиент-серверных приложений. Вы можете запускать клиент и сервер на одной машине, пользуясь этим адресом.

В стандартной поставке Java SE, в каталоге $JAVA_HOME/sample/nio/server, приведен полный пример HTTP/HTTPS-сервера, использующий средства NIO.

Работа с proxy-сервером

В настоящее время выход в Интернет часто осуществляется через proxy-сервер, кэширующий полученную информацию и дающий возможность нескольким машинам выходить в Интернет с одним и тем же IP-адресом. Для того чтобы создавать сокеты, работающие через proxy-сервер, начиная с JDK 5.0 в класс Socket добавлен конструктор

Socket(Proxy proxy);

Этот конструктор использует ссылку на объект абстрактного класса Proxy, устанавливающего связь с proxy-сервером. Объект создается конструктором

Proxy(Proxy.Type type, SocketAddress address);

в котором указывается тип proxy-сервера — одна из констант:

□ direct — соединение без proxy-сервера;

□ http — соединение с proxy-сервером, обслуживающим протокол HTTP или FTP;

□ socks — соединение через proxy-сервер, работающий по протоколу SOCKS4 или SOCKS5.

Класс SocketAddress, содержащий адрес и порт сервера, — это абстрактный класс. На практике при создании адреса используется его расширение InetSocketAddress.

Соберем все это вместе. Обычный способ создания сокета, работающего через proxy-сервер, выглядит так:

Socket sock = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("socks.domain.com", 1080)));

Упражнения

3. Напишите программу, передающую файлы по упрощенному протоколу FTP.

4. Установите связь через proxy-сервер с известными вам сайтами Интернета.

Работа по протоколу UDP

Для посылки дейтаграмм отправитель и получатель создают сокеты дейтаграммного

типа. В Java их представляет класс DatagramSocket. В классе три конструктора:

□ DatagramSocket () — создаваемый сокет присоединяется к любому свободному порту на локальной машине;

□ DatagramSocket (int port) - создаваемый сокет присоединяется к порту port на ло

кальной машине;

□ DatagramSocket (int port, InetAddress addr) — создаваемый сокет присоединяется к порту port; аргумент addr — один из адресов локальной машины.

Класс содержит массу методов доступа к параметрам сокета и, кроме того, методы отправки и приема дейтаграмм:

□ send ( DatagramPacket pack) -отправляет дейтаграмму, упакованную в пакет pack;

□ receive (DatagramPacket pack) - дожидается получения дейтаграммы и заносит ее в

пакет pack.

При обмене дейтаграммами соединение обычно не устанавливается, дейтаграммы посылаются наудачу, в расчете на то, что получатель ожидает их. Но можно установить соединение методом

connect(InetAddress addr, int port);

При этом устанавливается только одностороннее соединение с хостом по адресу addr и номером порта port — или на отправку, или на прием дейтаграмм. Потом соединение можно разорвать методом disconnect ( ).

При посылке дейтаграммы по протоколу UDP сначала создается сообщение в виде массива байтов, например:

String mes = "This is the sending message."; byte[] data = mes.getBytes();

Потом записывается адрес — объект класса inetAddress, например:

InetAddress addr = InetAddress.getByName(host);

Затем сообщение упаковывается в пакет — объект класса DatagramPacket. При этом указывается массив данных, его длина, адрес и номер порта:

DatagramPacket pack = new DatagramPacket(data, data.length, addr, port);

Далее создается дейтаграммный сокет:

DatagramSocket ds = new DatagramSocket();

и дейтаграмма отправляется:

ds.send(pack);

После посылки всех дейтаграмм сокет закрывается, не дожидаясь какой-либо реакции со стороны получателя:

ds.close();

Прием и распаковка дейтаграмм производится в обратном порядке, вместо метода send() применяется метод receive(DatagramPacket pack).

В листинге 24.5 показан пример класса Sender, посылающего сообщения, набираемые в командной строке, на localhost, порт номер 1050. Класс Recipient, описанный в листинге 24.6, принимает эти сообщения и выводит их в свой стандартный вывод.

Листинг 24.5. Посылка дейтаграмм по протоколу UDP

import java.net.*; import java.io.*;

public class Sender{

private String host; private int port;

Sender(String host, int port){ this.host = host; this.port = port;

private void sendMessage(String mes){ try{

byte[] data = mes.getBytes();

InetAddress addr = InetAddress.getByName(host);

DatagramPacket pack =

new DatagramPacket(data, data.length, addr, port); DatagramSocket ds = new DatagramSocket(); ds.send(pack); ds.close();

}catch(IOException e){

System.err.println(e);

}

}

public static void main(String[] args){

Sender sndr = new Sender("localhost", 1050); for (int k = 0; k < args.length; k++) sndr.sendMessage(args[k]);

}

}

Листинг 24.6. Прием дейтаграмм по протоколу UDP

import java.net.*; import java.io.*;

public class Recipient{

public static void main(String[] args){ try{

DatagramSocket ds = new DatagramSocket(1050); while (true){

DatagramPacket pack = new DatagramPacket(new byte[1024], 1024); ds.receive(pack);

System.out.println(new String(pack.getData()));

}

}catch(Exception e){

System.out.println(e);

}

}

}

Упражнение

5. Создайте аналог электронной почтовой связи с помощью дейтаграмм.

Вопросы для самопроверки

1. Что такое сетевая архитектура "клиент-сервер"?

2. Чем отличается клиентская часть приложения от серверной части?

3. Может ли клиентская часть приложения взаимодействовать сразу с несколькими серверами?

4. Что такое сетевой протокол?

5. Что такое стек протоколов?

6. Какие протоколы входят в стек протоколов TCP/IP?

7. Что такое IP-адрес?

8. Что такое номер порта?

9. Что такое дейтаграмма?

10. Что такое DNS-имя?

11. Что такое адрес URL?

12. Как установить связь через прокси-сервер?