Вы находитесь на странице: 1из 68

Сетевое взаимодействие

1
Терминология

● Вычислительная сеть — система для передачи данных


между вычислительными устройствами — узлами сети.
● Хост — компьютер, подключенный к сети и имеющий
сетевой адрес. Любой хост — узел сети. Любой узел сети
— не обязательно хост.
● Протокол — набор правил, определяющих порядок
действий и формат данных при сетевом обмене.

2
Сетевая архитектура

● Клиент-серверная архитектура
● Централизованное управление и обмен данными
● Сервер предоставляет сервисы в режиме ожидания запроса
● Клиент получает результат от сервера по запросу
● Надежность зависит от сервера — критический узел
● Одноранговая архитектура (пиринговая)
● Децентрализованное управление и обмен данными
● Все узлы (пиры) равноправны, могут быть клиентом и сервером
● Нет критического узла

3
Модель передачи данных

● Эталонная модель OSI


● Стек протоколов TCP/IP
TCP/IP OSI Пример

Прикладной HTTP

Прикладной Представительский

Сеансовый

Транспортный Транспортный TCP, UDP

Сетевой Сетевой IP

Канальный Ethernet
Канальный
Физический витая пара

4
Пакеты

● Пакет = заголовок + данные

Уровень Уровень + 1

З Д З Д

Уровень - 1 Уровень

З Данные З Данные

0100010000111101010

5
Сетевое взаимодействие в Java

● Протоколы транспортного уровня — TCP и UDP


● TCP
◊ устанавливается соединение
◊ подтверждение доставки
◊ надежность передачи данных
● UDP
◊ без установление соединения
◊ без подтверждения доставки
◊ скорость передачи данных
● Протокол прикладного уровня - HTTP

6
IP-адрес

● Идентифицирует связь между роутером и хостом


● ID сети (префикс) + ID хоста (суффикс)
● IPv4 — 32 бита (194.85.160.55)
● Класс A: префикс 8 бит (0...) + суффикс 24 бита
● Класс B: префикс 16 бит (10…) + суффикс 16 бит
● Класс C: префикс 24 бита (110…) + суффикс 8 бит
● Маска подсети: 192.85.160.55
◊ 192.168.0.5/255.255.255.240
◊ 192.168.0.5/28
● IPv6 — 128 бит [FC05::4429:0:AC02]
● Loopback (localhost - 127.0.0.1 / [::1]

7
DNS

● DNS — Domain Name Service


● Преобразование между доменным именем и IP-адресом
● www.google.com ↔ 172.217.23.132

8
Порт

● IP-адрес идентифицирует хост


● порт идентифицирует процесс (приложение)

● Для обмена данными нужно знать:


● протокол
● IP-адрес и порт отправителя
● IP-адрес и порт получателя
● Сокет — интерфейс для обмена данными — адрес и
порт

9
InetAddress

● Класс InetAddress
● Inet4Address Inet6Address
● Методы InetAddress (статические)
● InetAddress getLocalHost()
● InetAddress getByAddress(byte[] addr)
● InetAddress getByName(String name) — обращение к DNS
● Нестатические методы
● byte[] getAddress
● String getHostName()

10
InetSocketAddress

● InetSocketAddress(InetAddress addr, int port)


● InetSocketAddress(int port)
● InetSocketAddress(String hostname, int port)

11
Сетевой обмен

● Клиент
● Может работать на любом хосте (сервер не знает, где именно)
● Порт выбирается при отправлении запроса (первый свободный)
● Посылает запрос серверу, ждет ответ
● Запрос
● Содержит информацию о клиенте и данные
● Сервер
● Работает на определенном хосте (известный IP-адрес)
● Прослушивает определенный порт (зависит от сервиса)
● Ждет запрос от клиента, посылает ответ
● Ответ
● Содержит данные
12
Диаграмма последовательностей

Клиент Сервер

Синхронный запрос

Асинхронный ответ

13
Обмен по протоколу UDP

● java.net.DatagramPacket — дейтаграмма (передаваемые


данные + служебная информация)
● Адрес буфера
● Длина буфера
● Адрес получателя (при отправлении)
● java.net.DatagramSocket — сокет для обмена
● Порт для прослушивания (для получения)
● Адрес и порт (для отправления)

14
Пример обмена по протоколу UDP

// клиент // сервер

byte b[] = {0,1,2,3,4,5,6,7,8,9}; byte b[] = new byte[10];

SocketAddress a = SocketAddress a =
new InetSocketAddress(ADDR, PORT); new InetSocketAddress(PORT);
DatagramSocket s = DatagramSocket s =
new DatagramSocket(); new DatagramSocket();

DatagramPacket o = DatagramPacket i =
new DatagramPacket(b, b.length, a); new DatagramPacket(b, b.length);
s.send(o); s.receive(i);

for (int j = 0; j < 10; j++) {


b[j] *= 2;
}
DatagramPacket o =
DatagramPacket i = new DatagramPacket(b, b.length,
new DatagramPacket(b, b.length); i.getAddress(), i.getPort());
s.receive(i); s.send(o);

for (byte j : b) {
System.out.println(j);
}
15
Обмен по протоколу TCP

● java.net.Socket — сокет для обмена (клиент и сервер)


● new Socket (адрес + порт — для отправления)
● Socket ServerSocket.accept() - для получения
● java.net.ServerSocket — фабрика сокетов
● new ServerSocket(порт) — на стороне сервера
● обмен данными через потоки ввода-вывода
● Socket.getInputStream()
● Socket.getOutputStream()

16
Пример обмена по протоколу TCP

// клиент // сервер

byte b[] = {0,1,2,3,4,5,6,7,8,9}; byte b[] = new byte[10];


ServerSocket ss =
new ServerSocket(PORT);
Socket s = new Socket(ADDR, PORT); Socket s = ss.accept();

OutputStream o = s.getOutputStream(); InputStream i = s.getInputStream();


o.write(b); i.read(b);

for (int j = 0; j < 10; j++) {


b[j] *= 2;
}
InputStream i = s.getInputStream(); OutputStream o = s.getOutputStream();
i.read(b); o.write(b);
Arrays.stream(b)
.forEach(System.out::println());

i.close(); o.close(); s.close(); i.close(); o.close(); s.close();


ss.close();

17
Использование каналов и java.nio

● Протокол UDP — DatagramChannel


● open()
● bind(SocketAddress local)
● send(ByteBuffer, SocketAddress)
● receive(ByteBuffer)

18
Пример обмена по протоколу UDP (NIO)

// клиент // сервер

byte b[] = {0,1,2,3,4,5,6,7,8,9}; byte b[] = new byte[10];

SocketAddress a = SocketAddress a =
new InetSocketAddress(ADDR, PORT); new InetSocketAddress(PORT);
DatagramChannel s = DatagramChannel s =
DatagramChannel.open(); DatagramChannel.open();
s.connect(a); s.bind(a);
ByteBuffer f = ByteBuffer.wrap(b); ByteBuffer f = ByteBuffer.wrap(b);
f.flip(); f.clear();
s.send(f, a); a = s.receive(f);

for (int j = 0; j < 10; j++) {


b[j] *= 2;
}
f.clear(); f.flip();
a = s.receive(f); s.send(f, a);

for (byte j : b) {
System.out.println(j);
}

19
Использование каналов и java.nio

● Протокол TCP
● ServerSocketChannel
◊ open()
◊ bind(SocketAddress local)
◊ SocketChannel accept()
● SocketChannel
◊ open(SocketAddress remote)
◊ write(ByteBuffer)
◊ read(ByteBuffer)

20
Пример обмена по протоколу TCP (NIO)

// клиент // сервер

byte b[] = {0,1,2,3,4,5,6,7,8,9}; byte b[] = new byte[10];


SocketAddress a = SocketAddress a =
new InetSocketAddress(ADDR, PORT); new InetSocketAddress(PORT);
ServerSocketChannel ss =
ServerSocketChannel.open();
OutputStream o = s.getOutputStream(); ss.bind(a);
SocketChannel
o.write(b); s = SocketChannel s = ss.accept();
SocketChannel.open(a);
ByteBuffer f = ByteBuffer.wrap(b); ByteBuffer f = ByteBuffer.wrap(b);
f.flip(); f.clear();
s.write(f); s.read(f);
for (int j = 0; j < 10; j++) {
b[j] *= 2;
}
f.clear(); f.flip();
a = s.read(f); s.write(f);
s.close(); s.close();
ss.close();
Arrays.stream(b)
.forEach(System.out::println());
21
Неблокирующий ввод-вывод

● SocketChannel.configureBlocking(false)
● Методы read и write возвращают int — количество
прочитанных байт, или -1, если данных больше нет.

25
Протокол HTTP

● URI — Unified Resource Identifier


● URL — Unified Resource Locator
● URN — Unified Resource Name

26
Класс URL

URL url = new URL("http://www.google.com");


InputStream is = url.openStream();
is.read();
is.close();
Object o = url.getContent();

27
Класс URLConnection

URL url = new URL("http://www.google.com");


URLConnection uc = url.getConnection();
uc.connect();
InputStream is = uc.getInputStream();
// is.read();
uc.setDoOutput(true);
OutputStream os = uc.getOutputStream();
// os.write();
is.close(); os.close();
uc.close()
28
Функциональное программирование

29
Функциональное программирование

● Функции высшего порядка — могут быть аргументами


● Чистые функции — без побочных эффектов
● Вычисления без состояния — результат зависит только
от аргументов
● Ленивые вычисления — результат вычисляется только
когда он нужен
● Достоинства:
● Автоматическая многопоточность
● Более надежный код — простота тестирования
● Оптимизация

30
Функциональное программирование

● Итерация → Рекурсия
● Проблема — ограничение стека
● Вызов функции — параметры и адрес возврата — в стек
● Во время работы функции локальные переменные в стеке
● Возврат — очистка стека и переход по адресу возврата
● Решение — хвостовая рекурсия
● Рекурсивный вызов функции — последняя команда
● Вместо повторных рекурсивных вызовов — замена параметров
и возврат к началу (фактически - итерация)

31
Факториал (итерация)

public int factorial(int n) {


int f = 1;
for (int i = 2; i <= n; i++) {
f = f * i;
}
return f;
}

32
Факториал (рекурсия)

public int factorial(int n) {


return n <= 1 ? 1 : factorial(n — 1) * n;
}

33
Факториал (хвостовая рекурсия)

public int factorial(int n) {


return fx(n, 1);
}

public int fx(int n, int f) {


return n <= 1 ? f : fx(n — 1, f * n);
}

34
Элементы λ-исчисления

● Алонзо Чёрч (Alonzo Church)


● â.a+1 → ∧a.a+1 → Λa.a+1 → λa.a+1
● â — аргумент выражения a+1
● Переменная: x
● Операции:
● Абстракция: λx.f (связывание x с функцией f)
● Аппликация (применение): f g (применение f к аргументу g)

35
Пример

● inc(x) = x + 1 inc(3)
● f(x) = x + 1 f(3)
● (x) → x + 1 ((x) → x + 1) (3)
● λx.x+1

● Свободные и связанные переменные


● λx.x+y свободная

● (x) → x + y

связанная

36
Пример

● sum(x,y) = x + y sum(3,4)
● f(x,y) = x + y f(3,4)
● (x,y) → x + y (3,4) ((x,y) → x + y) (3,4)

● Каррирование
● (x,y) → x + y
● (y) → x + y
● (x) → ((y) → x + y)

37
Функции обратного вызова (callback)

● Передача функции с целью ее дальнейшего вызова


● реализация действия разными способами, выбираемыми во
время исполнения
● реализация асинхронной реакции на события
● Варианты реализации
● указатели на функцию (C, C++)
● делегаты (C#)
● объект интерфейса с методом / анонимный класс (Java < 8)
● λ-выражения (Java 8+)

38
Лямбда-выражения и функциональные интерфейсы

● λ-выражение — блок кода, предназначенный для


объявления анонимной функции.
● λ-выражение имеет тип функционального интерфейса
(целевой тип).
● Функциональный интерфейс — интерфейс, в котором
определен один и только один абстрактный метод (не
считая default и методов Object)
● λ-выражение можно присвоить переменной целевого типа.
● λ-выражение можно передать в метод, аргумент которого
имеет целевой тип.

39
Пример

@FunctionalInterface interface Comparator<T> {


public int compare(T obj1, T obj2);
}

public void sort(Comparator<T>) // метод сортировки

Comparator<String> comp;
comp = (s1, s2) -> s1.length() - s2.length();
sort(comp);

sort((String s1, String s2) -> s1.length() - s2.length());

40
Синтаксис λ-выражений

(параметры) -> выражение


(параметры) -> { инструкции; }
(int x, int y) -> x + y
(x, y) -> x * y
() -> 42
(String s) -> System.out.println(s)
x -> x / 2
c -> { int s = c.size(); c.clear(); return s; }
● тип параметров — либо явно, либо из контекста
● круглые скобки можно опускать, если контекстный параметр один

41
Захват переменных

● Область видимости λ-выражения = область видимости


окружающего блока
● В λ-выражении можно использовать только эффективно
финальные переменные из окружающего его блока
● λ-выражение захватывает значения переменных из
окружающего блока.
● λ-выражение + значения захваченных переменных =
замыкание (closure)
public static void repeatMessage(int count, String s) {
Runnable r = () -> {
for (int i = 0; i < count; i++) { println(s); }
}
new Thread(r).start();
}
42
Ссылка на метод

class Test {
Test(int x) { }
static void staticMethod(int x) { }
void instanceMethod(int x) { }
}
Test t = new Test(1);

x -> Test.staticMethod(x) Test::staticMethod


x -> t.instanceMethod(x) t::instanceMethod
(t,x) -> t.instanceMethod(x) Test::instanceMethod
x -> new Test(x) Test::new

43
Функциональные интерфейсы

● java.lang.Runnable
void run();
new Thread(() -> { … }).start();
● java.util.Comparator<T>
int compare(T o1, T o2);
Collections.sort(list, (x,y) -> y - 2 * x);
● java.util.function.* - набор функциональных интерфейсов
общего назначения для разных случаев

45
Пакет java.util.function

● Supplier<R> { R get() }
● Consumer<T> { void accept(T t) }
● Predicate<T> { boolean test(T t) }
● Function<T,R> { R apply(T t) }
● UnaryOperator<T> { T apply(T t) }
● BiFunction<T,U,R> { R apply(T t, U u) }
● BinaryOperator<T> { T apply(T t1, T t2) }

46
Supplier - Поставщик

Supplier<R> {
R get()
}

● () -> Instant.now()

● IntSupplier { int getAsInt() }


● LongSupplier { long getAsLong() }
● DoubleSupplier { double getAsDouble() }
● BooleanSupplier { boolean getAsBoolean() }

47
Consumer — Потребитель

Consumer<T> {
void accept(T t)
default Consumer andThen(Consumer after)
}

● s -> System.out.println(s)

● IntConsumer { void accept(int v) }


● LongConsumer { void accept(long v) }
● DoubleConsumer { void accept(double v) }

48
Predicate — Утверждение

Predicate<T> {
boolean test(T t)
default Predicate and(Predicate other)
default Predicate or(Predicate other)
default Predicate negate()
}

● list -> list.isEmpty()

● IntPredicate { boolean test(int v) }


● LongPredicate { boolean test(long v) }
● DoublePredicate { boolean test(double v) }

49
Function — Функция

Function<T,R> {
R apply(T t)
default Function andThen(Function after)
default Function compose(Function before)
default Function identity()
}

● array -> Arrays.asList(array)

● IntFunction<R> { R apply(int v) }
● LongFunction<R> { R apply(long v) }
● DoubleFunction<R> { R apply(double v) }

● ToIntFunction<T> { int applyAsInt(T t) }


● ToLongFunction<T> { long applyAsLong(T t) }
● ToDoubleFunction<T> { double applyAsDouble(T t) }

● IntToLongFunction { long applyAsLong(int v) }


● IntToDouble, LongToInt, LongToDouble, DoubleToInt, DoubleToLong

50
UnaryOperator — Унарный оператор

UnaryOperator<T> {
T apply(T t)
default UnaryOperator andThen(UnaryOperator after)
default UnaryOperator compose(UnaryOperator before)
default UnaryOperator identity()
}

● s -> String.toLowerCase(s)

● IntUnaryOperator { int apply(int v) }


● LongUnaryOperator { long apply(long v) }
● DoubleUnaryOperator { double apply(double v) }

51
BiConsumer

BiConsumer<T, U> {
void accept(T t, U u)
default BiConsumer andThen(BiConsumer after)
}

ObjIntConsumer<T> { void accept(T t, int v) }


ObjLongConsumer<T> { void accept(T t, long v) }
ObjDoubleConsumer<T> { void accept(T t, double v) }

52
BiPredicate

BiPredicate<T, U> {
boolean test(T t, U u)
default BiPredicate and(BiPredicate other)
default BiPredicate or(BiPredicate other)
default BiPredicate negate()
}

53
BiFunction

BiFunction<T,U,R> {
R apply(T t, U u)
default BiFunction andThen(BiFunction after)
}

ToIntBiFunction<T,U> { int applyAsInt(T t, U u) }


ToLongBiFunction<T,U> { long applyAsLong(T t, U u) }
ToDoubleBiFunction<T,U> { double applyAsDouble(T t, U u) }

54
BinaryOperator

BinaryOperator<T> {
T apply(T t, T t)
default BinaryOperator andThen(BinaryOperator after)
static BinaryOperator maxBy(Comparator comp)
static BinaryOperator minBy(Comparator comp)
}

IntBinaryOperator { int applyAsInt(int v1, int v2) }


LongBinaryOperator { long applyAsLong(long v1, long v2) }
DoubleBinaryOperator { double applyAsDouble(double v1, double v2) }

55
Пакет java.util.stream

● Конвейерная обработка данных


● Поток — последовательность элементов
● Поток может быть последовательным или параллельным
● Конвейер — последовательность операций

56
Конвейеры и коллекции

● Отличия конвейера от коллекции


● Элементы не хранятся
● Неявная итерация
● Функциональный стиль — операции не меняют источник
● Большинство операций работают с λ-выражениями
● Ленивое выполнение
● Возможность неограниченного числа элементов

57
Состав конвейера

● Конвейер =
● Источник
● Промежуточные операции (0 или больше)
● Завершающая операция (одна)

58
Пример

List<String> words

1) long count = 0;
for (String s : words) {
if (s.length() > 5) count++;
}

2) long count =
words.stream()
.filter(s -> s.length() > 5)
.count();

59
Источники конвейера

Способы получения потока из источника:


● Collection.stream()
● Collection.parallelStream()
● Arrays.stream(Object[])
● Stream.of(Object[])
● IntStream.range(int, int)
● Files.lines(Path)

● Stream.empty()
● Stream.generate(Supplier<T> s)
● Stream.iterate(T seed, UnaryOperator<T> f)

60
Промежуточные операции

● Промежуточные операции возвращают поток


● Промежуточные операции выполняются "лениво" —
фактически выполнение операции может быть
задержано.
● Промежуточные операции делятся на:
● Не хранящие состояние (stateless) — выполняются над
элементом вне зависимости от других элементов
● Хранящие состояние (stateful) — выполнение зависит от других
элементов (например, сортировка)

61
Завершающие операции

● Завершающие операции возвращают некий результат,


либо имеют побочное действие. После завершающей
операции поток прекращает существование.

62
Классы и интерфейсы

● интерфейс BaseStream
● void close()
● S parallel()
● S sequential()
● S unordered()
● Iterator iterator()
● Spliterator spliterator()

● Интерфейс Stream<T>
● Интерфейсы IntStream, LongStream, DoubleStream

63
Spliterator

● Параллельный Iterator
● Spliterator trySplit() — возвращает часть элементов как
отдельный сплитератор
● boolean tryAdvance(Consumer action) — выполнить операцию
для очередного элемента
● void forEachRemaining(Consumer action) — выполнить операцию
для всех оставшихся элементов

64
Промежуточные операции

● Методы для промежуточных операций (stateless)


● Stream<T> filter (Predicate<T> p)
● возвращает поток из элементов, соответствующих условию
● Stream<R> map(Function<T,R> mapper)
● преобразует поток элементов T в поток элементов R
● Stream<R> flatMap(Function <T, Stream<R>> mapper)
● преобразует каждый элемент потока T в поток элементов R
● Stream<T> peek(Consumer<T> action)
● выполняет действие для каждого элемента потока T

65
Промежуточные операции

● Методы для промежуточных операций (stateful)


● Stream<T> distinct()
● возвращает поток неповторяющихся элементов
● Stream<T> sorted(Comparator<T> comp)
● возвращает отсортированный поток
● Stream<T> limit(long size)
● возвращает усеченный поток из size элементов
● Stream<T> skip(long n)
● возвращает поток, пропустив n элементов

66
Завершающие операции

● void forEach(Consumer<T> action)


● void forEachOrdered(Consumer<T> action)
● выполняет действие для каждого элемента потока
● второй вариант гарантирует сохранение порядка элементов
● Optional<T> min(), Optional<T> max()
● возвращают минимальный и максимальный элементы,
● long count(), int (long, double) sum()
● возвращают количество и сумму элементов

● OptionalInt, OptionalLong, OptionalDouble


● int getAsInt(), long getAsLong(), double getAsDouble()

67
Класс java.util.Optional<T>

● Оболочка, которая может содержать или не содержать


значение
● boolean isPresent() - true, если значение есть
● T get() - возвращает значение
● Optional<T> of(T value) — возвращает оболочку со
значением
● T orElse(T other) — возвращает значение, если есть,
other, если нет

68
Завершающие операции

● reduce(BinaryOperator accumulator)
.stream
.reduce((a, b) -> a * b) // подсчет произведения
● collect(Collector collector)
.stream
.collect(Collectors.toList());

69
Завершающие операции

● Collectors
● toCollection(Supplier factory), toList(), toSet()
● toMap(Function k, Function v, BinaryOperator merge, Supplier factory)
● toConcurrentMap
● joining(String delimiter, String prefix, String suffix)
● Collector<T> mapping(Function<T,U> mapper, Collector<U> s)
● minBy(Comparator), maxBy(Comparator), counting()
● Collector<Integer> summingInt(ToIntFunction mapper)
● Collector<Integer> averagingInt(ToIntFunction mapper)
● Collector<IntSummaryStatistics> summarizingInt(ToIntFunction mapper)
● reducing(identity, Function<T,U> mapper, BinaryOperator op)
● Map<K,List<T>> groupingBy(Function<T,K> classifier)
● Map<K,D> groupingBy(Function<T,K> m, Supplier f, Collector<T,D> s)
● groupingByConcurrent
● Map<Boolean,List<T>> partitioningBy(Predicate<T> predicate)
● Map<Boolean,D> partitioningBy(Predicate<T> p, Collector<T,D> s)

70
Проверки

● boolean anyMatch(Predicate<T> p)
● истина, если условие выполняется хотя бы для одного элемента
● При нахождении первого совпадения прекращает проверку
● boolean allMatch(Predicate<T> p)
● истина, если условие выполняется для всех элементов.
● При нахождении первого несовпадения прекращает проверку
● boolean noneMatch(Predicate<T> p)
● истина, если условие не выполняется ни для одного элемента.
● При нахождении первого совпадения прекращает проверку

71
Пример

public static void main(String[] args) {


Arrays.asList(args).stream()
.filter(s -> s.length() < 5)
.map(String::toUpperCase)
.sorted()
.forEachOrdered(System.out::println);
}

72