
Лабораторная работа № 8
Работа в сети.
Цель работы: получить навыки создания сетевых приложений на языке Java.
Продолжительность работы - 4 ч.
Java делает сетевое программирование простым благодаря наличию специальных средств и класса Network. Основной используемый протокол − TCP/IP.
Приложения клиент/сервер используют компьютер, выполняющий специальную программу − сервер, которая предоставляет услуги другим программам − клиентам.
Клиент − это программа, получающая услуги от сервера. Клиент-серверные приложения основаны на использовании верхнего уровня протоколов.
Каждый компьютер по протоколу TCP/IP имеет уникальный IP-адрес. Это 32-
битовое число, обычно записываемое как четыре числа, разделенные точками, каждое из которых изменяется от 0 до 255. IP-адрес может быть временным и выделяться динамически для каждого подключения или быть постоянным, как для сервера.
Обычно при подключении к компьютеру вместо числового IP адреса используются символьные имена, называемые именами домена. Специальная программа DNS
(Domain Name Sever) преобразует имя домена в числовой IP-адрес. Получить IP-адрес
впрограмме можно с помощью объекта класса InetAddress из пакета java.net:
//вывод IP-адреса локального компьютера
import java.net.*; public class MyLocal {
public static void main(String[] args){ InetAddress myIP = null;
try {
myIP = InetAddress.getLocalHost();} catch (UnknownHostException e) {} System.out.println(myIP);
}
}
Метод getByName позволяет получить IP-адрес из имени домена: // извлечение IP-адреса из имени домена
import java.net.*;
public class IPfromDNS {
public static void main(String[] args){ InetAddress bsu = null;
try {
bsu = InetAddress.getByName("www.ya.ru"); } catch (UnknownHostException e){ } System.out.println(bsu);
}
}
Для явной идентификации услуг к IP-адресу присоединяется номер порта через двоеточие, например 217.21.43.2:31. Номера портов от 1 до 1024 используются,
например, для запуска двух программ серверов на одном компьютере. Если порт явно не указан, браузер воспользуется значением по умолчанию: 20 – FTP-данные, 21 –
FTP-управление, 23 – TELNET, 53 – DNS, 80 – HTTP, 110 – POP3, 119 – NNTP.
URL (Uniform Resource Locator)
Рассмотрим пример URL: http://ya.ru/
Здесь:
http — идентификатор протокола. Кроме HTTP существует некоторое количество других: File Transfer Protocol (FTP), Gopher, File, News.
ya.ru/ — имя ресурса. Слеш в конце — сокращение для «/index.html »)
Идентификатор протокола и имя ресурса должны быть разделены двоеточием и двумя прямыми слешами. Формат имени ресурса полностью зависит от используемого в конкретной ситуации протокола, однако, для многих протоколов (в
т.ч. и для HTTP) имя состоит из одного или нескольких следующих компонентов:
Имя хоста
Имя файла (путь к файлу на сервере)
Номер порта
Ссылка (слово или группа слов, к которым привязан гипертекстовая ссылка;
элемент HTML, связывающий веб-документы)
Для большинства протоколов обязательными для указания являются лишь первые два компонента.
В Java для представления URL есть специальный класс с одноименным названием из пакета java.net. Самый простой способ создать объект этого класса — передать строковое представление url в конструктор:
URL yandex = new URL("http://ya.ru/");
Созданный объект является абсолютным URL. Абсолютный в этом контексте означает, что указаны все параметры, необходимые для навигации к требуемому узлу. URL также может создаваться с использованием относительного (relative) имени.
Пример для адреса http://news.yandex.ru/Russia/
URL yandex = new URL("http://ya.ru/");
URL yandexRussia = new URL(yandex, "Russia/")
В случае, когда известны все 4 компонента адреса есть еще 2 конструктора (когда известен номер порта и нет):
new URL("http", "www.gamelan.com", "/pages/Gamelan.net.html");
new URL("http://www.gamelan.com/pages/Gamelan.net.html"); // эквивалентно
С номером порта:
new URL("http", "www.gamelan.com", 80, "pages/Gamelan.network.html");
// для адреса: http://www.gamelan.com:80/pages/Gamelan.network.html
Иногда адрес может содержать специальные символы (например, пробелы),
которые должны заменяться на их эквиваленты в URL (пробел = %20). Для автоматической обработки подобных случаев используется класс java.net.URI,
содержащий метод toURL() для преобразования в объект класса URL:
// сайт «http://foo.com/hello world/»
URI uri = new URI("http", "foo.com", "/hello world/", "");
URL url = uri.toURL();
Каждый из URL конструкторов использует класс-исключение
MalformedURLException.
Полезные методы класса URL:
getProtocol
Возвращает идентификатор протоколаgetAuthority
Возвращает доменный адрес вместе с портом (например ya.ru:80)
getHost
Возвращает адрес хостаgetPort
Возвращает номер порта. Если порт не установлен, возвращает -1
getPath
Возвращает путьgetQuery
Возвращает компонент query
getFile
Возвращает объединение getPath и getQuery (так, как они представлены в адресе)
getRef
Возвращает компонент URL ссылка.
Пример:
import java.net.*; import java.io.*;
public class ParseURL {
public static void main(String[] args) throws Exception {
URL aURL = new URL("http://java.sun.com:80/docs/books/tutorial" + "/index.html?name=networking#DOWNLOADING");
System.out.println("protocol = " + aURL.getProtocol());
System.out.println("authority = " + aURL.getAuthority());
System.out.println("host = " + aURL.getHost());
System.out.println("port = " + aURL.getPort());
System.out.println("path = " + aURL.getPath());
System.out.println("query = " + aURL.getQuery());
System.out.println("filename = " + aURL.getFile());
System.out.println("ref = " + aURL.getRef());
}
}
Результаты работы: protocol = http
authority = java.sun.com:80 host = java.sun.com
port = 80
path = /docs/books/tutorial/index.html query = name=networking
filename = /docs/books/tutorial/index.html?name=networking ref = DOWNLOADING
Метод openStream() возвращает поток для указанного URL (что полезно, к
примеру, для чтения данных с запрашиваемой веб-страницы).
Соединение с URL
После успешного создания объекта типа URL можно вызвать метод openConnection для получения объекта URLConnection (или одного из специфических подклассов протокола, например, java.net.HttpURLConnection). Он используется для установки каких-либо параметров, необходимых для настройки непосредственно перед подключением (вызовом URLConnection.connect).
Пример: try {
URL yahoo = new URL("http://www.yahoo.com/"); URLConnection yahooConnection = yahoo.openConnection(); yahooConnection.connect();
} catch (MalformedURLException e) { // new URL() failed
. . . |
|
} catch (IOException e) { |
// openConnection() failed |
. . . |
|
}
При этом нет необходимости явно вызывать метод connect, т.к. в java все операции, зависящие тем или иным образом от открытого соединения (например, getInputStream, getOutputStream), в случае необходимости сами неявно будут выполнять соединения.
Чтение и запись
Создав объект URLConnection, можно легко начать считывать данные из потока,
возвращаемого методом URLConnection.getInputStream (работая с ним как с обычным
IO-потоком)
URL yahoo = new URL("http://www.yahoo.com/");
URLConnection yc = yahoo.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
С записью всё аналогично, с тем лишь исключением, что перед получением потока (метод getOutputStream), необходимо установить параметр output в значение true (функция setDoOutput(true)).
URL url = new URL(args[0]);
URLConnection connection = url.openConnection(); connection.setDoOutput(true);
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
Сокеты и сокетные соединения
Сокеты − это сетевые разъемы, через которые осуществляются двунаправленные поточные соединения между компьютерами. Сокет определяется номером порта и IP-
адресом. При этом IP-адрес используется для идентификации компьютера, номер порта – для идентификации процесса, работающего на компьютере. Когда одно приложение знает сокет другого, создается сокетное соединение. Клиент пытается соединиться с сервером, инициализируя сокетное соединение. Сервер ждет, пока клиент не свяжется с ним. Первое сообщение, посылаемое клиентом на сервер,
содержит сокет клиента. Сервер в свою очередь создает сокет, который будет использоваться для связи с клиентом, и посылает его клиенту с первым сообщением.
После этого устанавливается коммуникационное соединение.
Сокетное соединение с сервером создается с помощью объекта класса Socket. При этом указывается IP-адрес сервера и номер порта (80 для HTTP). Если указано имя домена, то Java преобразует его с помощью DNS-сервера к IP-адресу:
try { socket = new Socket("localhost",8080); }
catch (IOException e){ System.out.println("ошибка: " + e); }
Сервер ожидает сообщения клиента и должен быть запущен с указанием определенного порта. Объект класса ServerSocket создается с указанием конструктору номера порта и ожидает сообщения клиента с помощью метода accept(), который возвращает сокет клиента:
Socket socket = null;
try { server = new ServerSocket(8080); socket = server.accept(); }
catch (IOException e) { System.out.println("ошибка: " + e); }
Клиент и сервер после установления сокетного соединения могут получать данные из потока ввода и записывать данные в поток вывода с помощью методов getInputStrеam() и getOutputStrеam() или к PrintStream для того, чтобы программа могла трактовать поток как выходные файлы.
В следующем примере для посылки клиенту строки "привет!" сервер вызывает метод getOutputStream() класса Socket. Клиент получает данные от сервера с помощью
метода getInputStream(). Для разъединения клиента и сервера после завершения работы сокет закрывается с помощью метода close() класса Socket. В данном примере сервер посылает клиенту строку «привет!» после чего разрывает связь.
import java.io.*; import java.net.*;
public class MyServerSocket{
public static void main(String[] args) throws Exception{ Socket s = null;
try {//посылка строки клиенту
ServerSocket server = new ServerSocket(8030); s = server.accept();
PrintStream ps = new PrintStream(s.getOutputStream()); ps.println("привет!");
ps.flush();
s.close(); // разрыв соединения
}catch (IOException e){System.out.println("ошибка: " + e); }
}
}
Получение клиентом строки: import java.io.*;
import java.net.*;
public class MyClientSocket {
public static void main(String[] args) { Socket socket = null;
try {//получение строки клиентом
socket = new Socket("www.bsu.by", 8080); BufferedReader dis = new BufferedReader(new InputStreamReader(socket.getInputStream())); String msg = dis.readLine(); System.out.println(msg);
} catch (IOException e) {System.out.println("ошибка: " + e); }
}