Comm/Docu/ru/Streams.odc

CommStreams

DEFINITION CommStreams;

	CONST
		done = 0;
		noSuchProtocol = 1;
		invalidLocalAdr = 2;
		invalidRemoteAdr = 3;
		networkDown = 4;
		localAdrInUse = 5;
		remoteAdrInUse = 6;

	TYPE
		Adr = POINTER TO ARRAY OF CHAR;

		Listener = POINTER TO ABSTRACT RECORD 
			(l: Listener) Accept (OUT s: Stream), NEW, ABSTRACT;
			(l: Listener) Close, NEW, ABSTRACT;
			(l: Listener) LocalAdr (): Adr, NEW, ABSTRACT
		END;

		Stream = POINTER TO ABSTRACT RECORD 
			(s: Stream) Close, NEW, ABSTRACT;
			(s: Stream) IsConnected (): BOOLEAN, NEW, ABSTRACT;
			(s: Stream) ReadBytes (VAR x: ARRAY OF BYTE; beg, len: INTEGER;
												OUT read: INTEGER), NEW, ABSTRACT;
			(s: Stream) RemoteAdr (): Adr, NEW, ABSTRACT;
			(s: Stream) WriteBytes (IN x: ARRAY OF BYTE; beg, len: INTEGER;
												OUT written: INTEGER), NEW, ABSTRACT
		END;

	PROCEDURE NewListener (protocol, localAdr: ARRAY OF CHAR; OUT l: Listener;
												OUT res: INTEGER);
	PROCEDURE NewStream (protocol, localAdr, remoteAdr: ARRAY OF CHAR; OUT s: Stream;
												OUT res: INTEGER);

END CommStreams.


Модуль CommStreams предоставляет интерфейсы двух типов: Stream и Listener. Интерфейс проектировался с расчетом на протоколы, ориентированные на надежность. Фактические коммуникационные каналы предоставляются конкретными реализациями потока и слушателя. Типичный пример - реализация TCP/IP, которая построена на сокетах. Расширять типы Stream и Listener нужно только при их реализации, но не в клиентских модулях. Далее по тексту описаны отдельно интерфейсы этих типов, которые предназначены для клиентских модулей и модулей реализации.
Интерфейс потоков неблокирующий. Причина такого подхода - в немодальной природе BlackBox Component Framework. Позволить одному компоненту блокировать целую систему (а следовательно, и пользователя) - это значит нарушить основные принципы построения среды. Реализация неблокирующих коммуникационных объектов проста, однако не согласована. К счастью, объектно-ориентированное программирование позволяет понятно реализовывать такие объекты.

Спецификация для клиентов

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

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

Существует два способа получить новый объект типа Stream, который представляет собой новое открытое соединение. Если подключение производится к партнеру, ожидающему запроса ("серверу"), то используется процедура NewStream. В качестве параметра передается имя протокола, который будет использован (или, точнее, имя модуля с драйвером для этого протокола), и адреса, идентифицирующие удаленный и локальный порты. Форматы этих адресов, которые передаются в виде строк символов, зависят от используемого протокола.

Если необходима возможность приема вызовов с другого места ("клиента"), сначала создается объект-слушатель. Такой слушатель используется для обнаружения запросов на подключение с других машин. Всякий раз в таком случае у слушателя может быть получен новый объект Stream. Собственно слушатель может быть использован до тех только пор, пока явно не будет закрыт.

Требования к реализациям

Реализации интерфейса должны определить все процедуры типов Stream и Listener в соответствии со спецификацией, описанной в следующей секции. Кроме того, для размещения объектов должны быть экспортированы две процедуры со следующими заголовками:

PROCEDURE NewStream (localAdr, remoteAdr: ARRAY OF CHAR; OUT s: Stream; OUT res: INTEGER)
PROCEDURE NewListener (localAdr: ARRAY OF CHAR; OUT l: Listener; OUT res: INTEGER)

Эти процедуры будут вызваны модулем CommStreams с помощью мезанизма метапрограммирования BlackBox. Например, если используется протокол TCP/IP, в качетстве названия протокола используется имя модуля с его драйвером, то есть, "CommTCP". Для создания слушателя вызывается "CommTCP.NewListener" с набором аргументов.


CONST done
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что действие было выполнено.

CONST noSuchProtocol
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что модуль, указанный в protocol, не найден, не может быть загружен или не реализует требуемый интерфейс.

CONST networkDown
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что сетевой ресурс, используемый заданным протоколом, недоступен и никакие подключения выполнены быть не могут.

CONST invalidLocalAdr
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что значение, указанное в localAdr, не может быть использовано; либо потому, что строка синтаксически неверна, либо потому, что указанный адрес не существует.

CONST localAdrInUse
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что значение, заданное в localAdr, не может быть использовано, поскольку адрес уже используется.

CONST invalidRemoteAdr
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что значение, заданное как remoteAdr, не может быть использовано, или потому, что строка синтаксически некорректна, или потому, что заданный адрес не существует.

CONST remoteAdrInUse
Предопределенное значение для параметра res в NewStream и NewListener. Показывает, что значение, указанное в remoteAdr, не может быть использовано, поскольку адрес уже используется.


TYPE Adr
Динамический строковый тип для имен локальных или удаленных адресов.


TYPE Listener
ABSTRACT
Объекты типа Listener служат для работы с сетью.

PROCEDURE (l: Listener) Accept (OUT s: Stream)
NEW, ABSTRACT
Создает новый поток Stream, когда удаленный узел запрашивает подключение. Accept никогда не блокирует выполнение: он возвращает либо новый поток, либо NIL, если запроса на подключение не было. Если слушатель закрыт, Accept всегда возвращает NIL.

Постусловие
s = NIL
	никакого подключения не запрашивалось
s # NIL
	из сети было запрошено подключение

PROCEDURE (l: Listener) Close
NEW, ABSTRACT
Закрывает слушатель, так что больше никакие запросы из сети не принимаются. Close не дает эффекта, если слушатель был уже закрыт.

PROCEDURE (l: Listener) LocalAdr
NEW, ABSTRACT
Возвращает имя локального порта, за которым следит слушатель.


TYPE Stream
ABSTRACT
Объекты типа Stream представляют собой полностью дуплексные соединения.

PROCEDURE (s: Stream) Close
NEW, ABSTRACT
Закрывает соединение. Если соединение было уже закрыто, Close не дает эффекта.

Постусловие
~s.IsConnected()

PROCEDURE (s: Stream) IsConnected (): BOOLEAN
NEW, ABSTRACT
Проверяет, когда соединение все еще открыто, или оно было закрыто на другом конце, и никакие данные больше прочитать нельзя.
IsConnected не обязана быть точной по времени. Она только представляет текущие сведения о состоянии соединения. В действительности это состояние могло уже измениться. Это значит, что IsConnected может все еще возвращать TRUE, даже если соединение уже было закрыто.

Постусловие
result = FALSE
	соединение было закрыто одним из партнеров
result = TRUE
	возможно, соединение еще открыто

PROCEDURE (s: Stream) ReadBytes (VAR x: ARRAY OF BYTE; beg, len: INTEGER;
																OUT read: INTEGER)
NEW, ABSTRACT
Принимает не более len байт из соединения и читает их. Процедура никогда не блокирует выполнение. Никакие данные не читаются, если ~s.Connected().

Предусловие
beg >= 0	20
len > 0	21
LEN(x) >= beg + len	22

Постусловие
read >= 0
x[beg ... beg + read[ = bytes read

PROCEDURE (s: Stream) WriteBytes (IN x: ARRAY OF BYTE; beg, len: INTEGER;
																OUT written: INTEGER)
NEW, ABSTRACT
Посылает не более len байт через соединение. Процедура никогда не блокирует выполнение. Какое количество байт будет записано, может зависеть от внутренних ресурсов, например, размера буфера. Однако, нет гарантии, что получение данных партнером было уже выполнено. Никакие данные не пишутся, если ~s.Connected().

Предусловие
beg >= 0	20
len > 0	21
LEN(x) >= beg + len	22

Постусловие
written >= 0
x[beg ... beg + written[ = bytes written


PROCEDURE  NewListener (protocol, localAdr: ARRAY OF CHAR; OUT l: Listener; OUT res: INTEGER)
Начинает прием подключений на порту, заданном localAdr с использованием протокола передачи protocol. Формат localAdr зависит от используемого протокола.
Для TCP/IP, protocol = "CommTCP" и localAdr - это номер локального порта (может быть пустой строкой). Код возврата (параметр res) может принимать одно из следующих (см. объявления констант вверху) или специфичных для протокола значений:
	done, noSuchProtocol, networkDown,  invalidLocalAdr, localAdrInUse

Предусловие
protocol # ""	20

Постусловие
res = 0
	l # NIL
	listener is open
res # 0
	l = NIL

PROCEDURE  NewStream (protocol, localAdr, remoteAdr: ARRAY OF CHAR;
												OUT s: Stream; OUT res: INTEGER)
Подключается к узлу и порту, заданным remoteAdr через локальный порт localAdr. Формат адресов зависит от используемого протокола. Например, для protocol = "CommTCP" , localAdr должен быть номером порта, например, 4. Если передается пустая строка, используется номер порта 0 ("не важно, какой"). remoteAdr должен быть либо IP-адресом наподобие "100.0.0.7", или именем узла. После того и другого может быть поставлено ":" и номер порта, например, "100.0.0.7:2". Без этого параметра принимается номер порта 0.
Для TCP/IP, protocol = "CommTCP" и localAdr - номер локального порта (может быть пустой строкой).
Код возврата (параметр res) может принимать одно из следующих (см. объявления констант вверху) или специфичных для протокола значений:
	done, noSuchProtocol, networkDown, invalidLocalAdr, localAdrInUse, invalidRemoteAdr, remoteAdrInUse

Предусловие
protocol # ""	20

Постусловие
res = 0
	s # NIL
	поток открыт
res # 0
	s = NIL