русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Тема 20 Пакет java.net. Робота по протоколу TCP та UDP. Клас InetAddress


Дата додавання: 2014-05-29; переглядів: 1325.


План

1 Вступ

2 Обробка виняткової ситуації при з’єднанні

 

1 Вступ

Пакет java.net також надає доступ до протоколів більше низького рівня - TCP і UDP. Для цього спочатку треба ознайомиться із класом InetAddress, що є інтернет-адресою, або IP. Екземпляри цього класу створюються не за допомогою конструкторів, а за допомогою статичних методів:

 

InetAddress getLocalHost()

InetAddress getByName(String name)

InetAddress[] getAllByName(String name)

 

Перший метод повертає IP-Адреса машини, на якій виконується Java-Програма. Другий метод повертає адресу сервера, чиє ім'я передається як параметр. Це може бути як DNS-Ім'я, так і числовий IP, записаний у вигляді тексту, наприклад, "67.11.12.101". Нарешті третій метод визначає всі IP-Адреси зазначеного сервера.

Для роботи з TCP-Протоколом використовуються класи Socket і ServerSocket. Першим створюється ServerSocket - сокет на стороні сервера. Його найпростіший конструктор має тільки один параметр - номер порту, на якому будуть прийматися вхідні запити. Після створення викликається метод accept(), що припиняє виконання програми й очікує, поки який-небудь клієнт не инициализирует з'єднання. У цьому випадку робота сервера відновляється, а метод повертає екземпляр класу Socket для взаємодії із клієнтом:

 

try {

ServerSocket ss = new ServerSocket(3456);

Socket client=ss.accept(); // Метод не повертає керування, поки не

підключиться клієнт

} catch (IOException e) {

e.printStackTrace();

}

 

Клієнт для підключення до сервера також використовується клас Socket. Його найпростіший конструктор приймає два параметри - адреса сервера (у вигляді рядка або екземпляра InetAddress) і номер порту. Якщо сервер прийняв запит, то сокет конструюється успішно, і далі можна скористатися методами getInputStream() або getOutputStream().

try {

Socket s = new Socket("localhost", 3456);

InputStream is = s.getInputStream();

is.read();

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

2 Обробка виняткової ситуації при з’єднанні

Зверніть увагу на обробку виняткової ситуації UnknownHostException, що буде генеруватися, якщо віртуальна машина за допомогою операційної системи не зможе розпізнати зазначену адресу сервера у випадку, якщо він заданий рядком. Якщо ж він заданий екземпляром InetAddress, то цю помилку треба обробляти при виклику статичних методів цього класу.

На стороні сервера клас Socket використовується точно в такий же спосіб - через методи getInputStream() і getOutputStream(). Приведемо більше повний приклад:

 

import java.io.*;

import java.net.*;

public class Server {

public static void main(String args[]) {

try {

ServerSocket ss = new ServerSocket(3456);

System.out.println("Waiting...");

Socket client=ss.accept();

System.out.println("Connected");

client.getOutputStream().write(10);

client.close();

ss.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

 

Сервер по запиті клієнта відправляє число 10 і завершує роботу. Зверніть увагу, що при завершенні викликаються методи close() для відкритих сокетів. Клас клієнта:

 

import java.io.*;

import java.net.*;

public class Client {

public static void main(String args[]) {

try {

Socket s = new Socket("localhost", 3456);

InputStream is = s.getInputStream();

System.out.println("Read: "+is.read());

s.close();

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

 

Після запуску сервера, а потім клієнта, можна побачити результат - отримане число 10, після чого обидві програми закриються.

Розглянемо ці класи більш докладно. По-перше, клас ServerSocket має конструктор, у який передається крім номера порту ще й адреса машини. Це може здатися дивним, адже сервер відкривається на тій же машині, де працює програма, навіщо спеціально вказувати її адресу? Однак якщо комп'ютер має кілька мережних інтерфейсів (мережних карток), те він має й кілька мережних адрес. За допомогою такого деталізованого конструктора можна вказати, по якій саме адресі очікувати підключення. Це повинен бути саме локальна адреса машини, інакше виникне помилка.

Аналогічно клас Socket має розширений конструктор для вказівки як локальної адреси, з якого буде встановлюватися з'єднання, так і локального порту (інакше операційна система виділяє довільний вільний порт).

По-друге, можна скористатися методом setSoTimeout(int timeout) класу ServerSocket, щоб указати час у миллисекундах, протягом якого потрібно очікувати підключення клієнта. Це дозволяє не "зависати" серверу, якщо ніхто не намагається почати з ним працювати. Таймаут задається в миллисекундах, нульове значення означає нескінченний час очікування.

Важливо підкреслити, що після встановлення з'єднання із клієнтом сервер виходить із методу accept(), тобто перестає бути готів приймати нові запити. Однак як правило бажано, щоб сервер міг працювати з декількома клієнтами одночасно.

Для цього необхідно при підключенні чергового користувача створювати новий потік виконання, що буде обслуговувати його, а основний потік знову ввійде в метод accept(). Приведемо приклад такого рішення:

 

import java.io.*;

import java.net.*;

public class NetServer {

public static final int PORT = 2500;

private static final int TIME_SEND_SLEEP = 100;

private static final int COUNT_TO_SEND = 10;

private ServerSocket servSocket;

public static void main(String[] args) {

NetServer server = new NetServer();

server.go();

}

public NetServer() {

try{

servSocket = new ServerSocket(PORT);

}catch(IOException e){

System.err.println("Unable to open Server Socket : " + e.toString());

}

}

public void go() {

// Клас-Потік для роботи із клієнтом, що підключився

class Listener implements Runnable{

Socket socket;

public Listener(Socket aSocket){

socket = aSocket;

}

public void run(){

try{

System.out.println("Listener started");

int count = 0;

OutputStream out = socket.getOutputStream();

OutputStreamWriter writer = new OutputStreamWriter(out);

PrintWriter pWriter = new PrintWriter(writer);

while(count<COUNT_TO_SEND){

count++;

pWriter.print(((count>1)?",":"")+ "Say" + count);

sleeps(TIME_SEND_SLEEP);

}

pWriter.close();

}catch(IOException e){

System.err.println("Exception : " + e.toString());

}

}

}

// Основний потік, що циклічно виконує метод accept()

System.out.println("Server started");

while(true){

try{

Socket socket = servSocket.accept();

Listener listener = new Listener(socket);

Thread thread = new Thread(listener);

thread.start();

}catch(IOException e){

System.err.println("IOException : " + e.toString());

}

}

}

public void sleeps(long time) {

try{

Thread.sleep(time);

}catch(InterruptedException e){

}

}

}

 

Тепер оголосимо клієнта. Ця програма буде запускати кілька потоків, кожний їх яким незалежно підключається до сервера, зчитує його відповідь і виводить на консоль.

 

import java.io.*;

import java.net.*;

public class NetClient implements Runnable{

public static final int PORT = 2500;

public static final String HOST = "localhost";

public static final int CLIENTS_COUNT = 5;

public static final int READ_BUFFER_SIZE = 10;

private String name = null;

public static void main(String[] args) {

String name = "name";

for(int i=1; i<=CLIENTS_COUNT; i++){

NetClient client = new NetClient(name+i);

Thread thread = new Thread(client);

thread.start();

}

}

public NetClient(String name) {

this.name = name;

}

public void run() {

char[] readed = new char[READ_BUFFER_SIZE];

StringBuffer strBuff = new StringBuffer();

try{

Socket socket = new Socket(HOST, PORT);

InputStream in = socket.getInputStream();

InputStreamReader reader = new InputStreamReader(in);

while(true){

int count = reader.read(readed, 0, READ_BUFFER_SIZE);

if(count==-1)break;

strBuff.append(readed, 0, count);

Thread.yield();

}

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

System.out.println("client " + name + " read : " + strBuff.toString());

}

}

 

Розглянемо UDP. Для роботи із цим протоколом і на стороні клієнта, і на стороні сервера використовується клас DatagramSocket. У нього є наступні конструктори:

 

DatagramSocket()

DatagramSocket(int port)

DatagramSocket(int port, InetAddress laddr)

 

При виклику першого конструктора сокет відкривається на довільному доступному порту, що доречно для клієнта. Конструктор з одним параметром, що задає порт, як правило застосовується на серверах, щоб клієнти знали, на якому порту їм потрібно намагатися встановлювати з'єднання. Нарешті, останній конструктор необхідний для машин, у яких є присутнім кілька мережних інтерфейсів.

Після відкриття сокетів починається обмін датаграмами. Вони представляються екземплярами класу DatagramPacket. При відсиланні повідомлення застосовується наступний конструктор:

 

DatagramPacket(byte[] buf, int length, InetAddress address, int port)

 

Масив містить дані для відправлення (створений пакет буде мати довжину рівну length), а адреса й порт указують одержувача пакета. Після цього викликається метод send() класу DatagramSocket.

 

try {

DatagramSocket s = new DatagramSocket();

byte data[]={1, 2, 3};

InetAddress addr = InetAddress.getByName("localhost");

DatagramPacket p = new DatagramPacket(data, 3, addr, 3456);

s.send(p);

System.out.println("Datagram sent");

s.close();

} catch (SocketException e) {

e.printStackTrace();

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

 

Для одержання датаграми також створюється екземпляр класу DatagramPacket, але в конструктор передається лише масив, у який будуть записані дані після одержання (також вказується очікувана довжина пакета). Сокет необхідно створити із вказівкою порту, інакше швидше за все повідомлення просто не дійде до адресата. Використовується метод receive() класу DatagramSocket (аналогічно методу ServerSocket.accept() цей метод також перериває виконання потоку, поки не прийде запит від клієнта). Приклад реалізації одержувача:

 

try {

DatagramSocket s = new DatagramSocket(3456);

byte data[]=new byte[3];

DatagramPacket p = new DatagramPacket(data, 3);

System.out.println("Waiting...");

s.receive(p);

System.out.println("Datagram received: "+data[0]+", "+data[1]+", "+data[2]);

s.close();

} catch (SocketException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

Якщо запустити спочатку одержувача, а потім відправника, то можна побачити, що перший надрукує вміст отриманої датаграми, а потім програми завершать свою роботу.

На закінчення приведемо приклад сервера, що одержує датаграми й відправляє їх обернено, дописавши до них слово received.

 

import java.io.*;

import java.net.*;

public class DatagramDemoServer {

public static final int PORT = 2000;

private static final int LENGTH_RECEIVE = 1;

private static final byte[] answer = ("received").getBytes();

private DatagramSocket servSocket = null;

private boolean keepRunning = true;

public static void main(String[] args) {

DatagramDemoServer server = new DatagramDemoServer();

server.service();

}

public DatagramDemoServer() {

try{

servSocket = new DatagramSocket(PORT);

}catch(SocketException e){

System.err.println("Unable to open socket : " + e.toString());

}

}

protected void service() {

DatagramPacket datagram;

InetAddress clientAddr;

int clientPort;

byte[] data;

while(keepRunning){

try{

data = new byte[LENGTH_RECEIVE];

datagram = new DatagramPacket(data, data.length);

servSocket.receive(datagram);

clientAddr = datagram.getAddress();

clientPort = datagram.getPort();

data = getSendData(datagram.getData());

datagram = new DatagramPacket(data, data.length, clientAddr, clientPort);

servSocket.send(datagram);

}catch(IOException e){

System.err.println("I/O Exception : " + e.toString());

}

}

}

protected byte[] getSendData(byte b[]) {

byte[] result = new byte[b.length+answer.length];

System.arraycopy(b, 0, result, 0, b.length);

System.arraycopy(answer, 0, result, b.length, answer.length);

return result;

}

}



 

Список літератури

 

1 Роберт Орфали, Дэн Харки Java и CORBA в приложениях клиент-сервер. — М.: Лори, 2010. — 734 с.

2 Стивен Стелтинг Java без сбоев. Обработка исключений, тестирование, отладка. — СПб.: Кудиц-Образ, 2005. — 464 с.

3 Х.М. Дейтел, П. Дж. Дейтел Как программировать на Java. Книга 2. Файлы, сети, базы данных. — СПб.: Бином-Пресс, 2006. — 672 с.

4 В. Монахов Язык программирования Java и среда NetBeans. — СПб.: БХВ-Петербург, 2011. — 704 с.

5 Кей С. Хорстманн, Гари Корнелл Библиотека профессионала. Java 2. — М.: Вильямс, 2008. — 1168 с.

6 Брюс Эккель Филосифия Java. — СПб.: Питер, 2011. — 640 с.

7 Мартин К. Соломон, Нирва Мориссо-Леруа Oracle. Программирование на языке Java. — М.: Лори, 2010. — 512 с.

8 Тимур Машнин Современные Java-технологии на практике. — СПб.: БХВ-Петербург, 2010. — 560 с.

9 Васильев А.Н. Самоучитель Java с примерами и программами. — М.: Наука и техника, 2011. — 352 с.


 


<== попередня лекція | наступна лекція ==>
Тема 19 Пакет java.net. Використання класу URLConnection. Завантаження даних на сервер. Обов'язкова контрольна робота | ТЕМА 1. Поняття і сутність менеджменту.


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн