Пришло время рассказать о еще одной стороне применения замечательного языка Форт.
Этим циклом статей я покажу, как с его помощью можно создавать крошечные клиент-серверные приложения. Каждое из которых можно использовать как исследовательский и обучающий инструмент.
Для развлечения нам понадобится Windows вплоть до семерки и пакет SP-Forth. Андрей Черезов декларировал возможность запуска его форт-системы под Линуксом, но я это не проверял.
Для начала попробуем создать простейшую серверную программку, которая будет отдавать присоединившемуся клиенту ну… скажем текущую дату и время на сервере.
Создадим в блокноте или любом другом текстовом редакторе (Akelpad) файл datetimes.f в той же папке, где лежит файл spf4.exe.
Что же нам понадобится для создания сервера? Сокеты!
Чтобы начать с ними работать надо подключить соответствующую библиотеку.
~nn\lib\sock2.f
Удобно, все библиотеки хранятся в исходных текстах. Всегда можно заглянуть и посмотреть что это такое и как работает.
Первые две строчки — очевидно определение переменных. VALUE переменные являются усовершенствованием классического Форта. Они позволяют избежать бесконечных разыменований (получения значения по адресу) и сделать текст более читаемым.
Третья строчка говорит сама за себя, по последней же надо дать пару комментариев.
Слово CreateSoket понятно и так, но есть нюансы. Сокеты-то бывают разные! Честно сказать в сортах сокетов я не слишком разбираюсь, но самый простой и, похоже, самый часто используемый это блокирующий двунаправленный потоковый TCP/IP сокет, который создается этим словом. Как создать сокеты другого типа, можно заглянуть в библиотеку. Магическое слово THROW всего лишь перехватывает коды ошибок и генерирует исключение, если код ошибки отличен от нуля. Скобочки печатают на терминале текст между ними, CR — перевод строки.
Вот почти все. Теперь мы сидим и слушаем на порту 80, пока кто-нибудь не постучится.
Безусловно, можно выбрать совершенно произвольный свободный порт.
Слово AcceptSoket остановит выполнение программы до момента подключения.
Кто же к нам подключился?
выведет в консоль IP адрес и порт с которого произошло подключение. Единственный SWAP во всей программе возник только из-за не совсем продуманной реализации слова GetPeerIP&Port.
Подготовим дату и время к отправке.
Нам понадобится еще одна библиотека
lib\include\facil.f
Из которой нужно слово TIME&DATE. ( Пришлось его исправить в библиотеке, теперь оно отличается от комплектного. Оно стало выдавать миллисекунды, а еще изменился порядок даты. ) Это слово кладет на стек 7 значений. Миллисекунды, секунды, часы, год, месяц, число. Соответственно, первым со стека будет снято число. Имеется ввиду число месяца.
Выглядит ужасно. Прелесть Форта в том, что это безобразие можно превратить в конфетку.
Видно, что один и тот же текст повторяется 7 и два раза по 3. Конечно, можно облечь в цикл, но тогда текст будет трудно читаем.
Воспользуемся возможностями Форта.
Определим дополнительные слова
Логично, понятно и легко читаемо. { Ремарка: Слово S>D расширяет знаком верхний элемент стека до двойной точности. Слово (D.) преобразует число двойной точности в строку и кладет на стек её адрес и счетчик.}
По-прежнему ужасно. Что же можно сделать?
На помощь приходит одна ключевая особенность языка Форт. Мы можем создавать свои собственные определяющие слова.
Слово make_value во время своего исполнения выберет из входного потока следующий набор литер, ограниченный пробелами и поместит его на вершину текущего словаря. Семантику этому новоиспеченному слову задаст слово после DOES>. То-есть у нас есть инструмент, с помощью которого можно создавать понятия, объединенные общей семантикой.
И для разделителей
Теперь можем написать красиво
Рвем соединение
И очищаем сокеты
Все.
Как можно видеть этапы исполнения и компиляции могут чередоваться. Это еще одна базовая особенность Форта. Также хорошо написанная Форт-программа практически не требует комментариев.
Кто был достаточно внимателен, то заметил что мы переопределили базовые слова двоеточие и минус. Как быть с этим? Ответ ниже.
Этим циклом статей я покажу, как с его помощью можно создавать крошечные клиент-серверные приложения. Каждое из которых можно использовать как исследовательский и обучающий инструмент.
Для развлечения нам понадобится Windows вплоть до семерки и пакет SP-Forth. Андрей Черезов декларировал возможность запуска его форт-системы под Линуксом, но я это не проверял.
Для начала попробуем создать простейшую серверную программку, которая будет отдавать присоединившемуся клиенту ну… скажем текущую дату и время на сервере.
Создадим в блокноте или любом другом текстовом редакторе (Akelpad) файл datetimes.f в той же папке, где лежит файл spf4.exe.
Что же нам понадобится для создания сервера? Сокеты!
Чтобы начать с ними работать надо подключить соответствующую библиотеку.
~nn\lib\sock2.f
Удобно, все библиотеки хранятся в исходных текстах. Всегда можно заглянуть и посмотреть что это такое и как работает.
0 VALUE sockt 0 VALUE sockt_a SocketsStartup THROW .( Sockets started) CR CreateSocket THROW TO sockt .( Socket created) CR
Первые две строчки — очевидно определение переменных. VALUE переменные являются усовершенствованием классического Форта. Они позволяют избежать бесконечных разыменований (получения значения по адресу) и сделать текст более читаемым.
Третья строчка говорит сама за себя, по последней же надо дать пару комментариев.
Слово CreateSoket понятно и так, но есть нюансы. Сокеты-то бывают разные! Честно сказать в сортах сокетов я не слишком разбираюсь, но самый простой и, похоже, самый часто используемый это блокирующий двунаправленный потоковый TCP/IP сокет, который создается этим словом. Как создать сокеты другого типа, можно заглянуть в библиотеку. Магическое слово THROW всего лишь перехватывает коды ошибок и генерирует исключение, если код ошибки отличен от нуля. Скобочки печатают на терминале текст между ними, CR — перевод строки.
80 sockt BindSocket THROW .( Socket binded) CR sockt ListenSocket THROW .( Listen) CR sockt AcceptSocket THROW TO sockt_a .( Accept connection from:)
Вот почти все. Теперь мы сидим и слушаем на порту 80, пока кто-нибудь не постучится.
Безусловно, можно выбрать совершенно произвольный свободный порт.
Слово AcceptSoket остановит выполнение программы до момента подключения.
Кто же к нам подключился?
sockt_a GetPeerIP&Port THROW SWAP NtoA TYPE ." port:" .
выведет в консоль IP адрес и порт с которого произошло подключение. Единственный SWAP во всей программе возник только из-за не совсем продуманной реализации слова GetPeerIP&Port.
Подготовим дату и время к отправке.
Нам понадобится еще одна библиотека
lib\include\facil.f
Из которой нужно слово TIME&DATE. ( Пришлось его исправить в библиотеке, теперь оно отличается от комплектного. Оно стало выдавать миллисекунды, а еще изменился порядок даты. ) Это слово кладет на стек 7 значений. Миллисекунды, секунды, часы, год, месяц, число. Соответственно, первым со стека будет снято число. Имеется ввиду число месяца.
S" Current time: sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( day) S" -" sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( month) S" -" sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( year) S" " sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( hours) S" -" sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( minutes) S" -" sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( seconds) S" -" sockt_a WriteSocket THROW S>D (D.) sockt_a WriteSocket THROW ( milliseconds)
Выглядит ужасно. Прелесть Форта в том, что это безобразие можно превратить в конфетку.
Видно, что один и тот же текст повторяется 7 и два раза по 3. Конечно, можно облечь в цикл, но тогда текст будет трудно читаем.
Воспользуемся возможностями Форта.
Определим дополнительные слова
: send_value S>D (D.) sockt_a WriteSocket THROW ; : send_delimeter sockt_a WriteSocket THROW ;
Логично, понятно и легко читаемо. { Ремарка: Слово S>D расширяет знаком верхний элемент стека до двойной точности. Слово (D.) преобразует число двойной точности в строку и кладет на стек её адрес и счетчик.}
: Day send_value ; : Month send_value ; : Year send_value ; : Hours send_value ; : Minutes send_value ; : Seconds send_value ; : Milliseconds send_value ;
По-прежнему ужасно. Что же можно сделать?
На помощь приходит одна ключевая особенность языка Форт. Мы можем создавать свои собственные определяющие слова.
: make_value CREATE DOES> DROP send_value ; : make_values 0 DO make_value LOOP ;
Слово make_value во время своего исполнения выберет из входного потока следующий набор литер, ограниченный пробелами и поместит его на вершину текущего словаря. Семантику этому новоиспеченному слову задаст слово после DOES>. То-есть у нас есть инструмент, с помощью которого можно создавать понятия, объединенные общей семантикой.
7 make_values Day Month Year Hours Minutes Seconds Milliseconds
И для разделителей
: make_delimeter CREATE , DOES> 1 send_delimeter ; : make_delimeters 0 DO make_delimeter LOOP ; BL CHAR - CHAR : 3 make_delimeters : - _
Теперь можем написать красиво
TIME&DATE Day - Month - Year _ Hours : Minutes : Seconds : Milliseconds
Рвем соединение
sockt_a CloseSocket THROW
И очищаем сокеты
SocketsCleanup THROW
Все.
Как можно видеть этапы исполнения и компиляции могут чередоваться. Это еще одна базовая особенность Форта. Также хорошо написанная Форт-программа практически не требует комментариев.
Кто был достаточно внимателен, то заметил что мы переопределили базовые слова двоеточие и минус. Как быть с этим? Ответ ниже.
Причесанный код
~nn\lib\sock2.f lib\include\facil.f 80 CONSTANT port 0 VALUE sockt 0 VALUE sockt_a : send_value S>D (D.) sockt_a WriteSocket THROW ; : send_delimeter sockt_a WriteSocket THROW ; : make_value CREATE DOES> DROP send_value ; : make_values 0 DO make_value LOOP ; 7 make_values Day Month Year Hours Minutes Seconds Milliseconds : make_delimeter CREATE , DOES> 1 send_delimeter ; : make_delimeters 0 DO make_delimeter LOOP ; : define: : ; BL CHAR - CHAR : 3 make_delimeters : - _ define: time&date_server SocketsStartup THROW ." Sockets started" CR CreateSocket THROW TO sockt ." Socket created" CR port sockt BindSocket THROW ." Socket binded" CR sockt ListenSocket THROW ." Listen" CR sockt AcceptSocket THROW TO sockt_a ." Accept connection from: " sockt_a GetPeerIP&Port THROW SWAP NtoA TYPE ." port:" . S" Current time:" sockt_a WriteSocket THROW TIME&DATE Day - Month - Year _ Hours : Minutes : Seconds : Milliseconds sockt_a CloseSocket THROW SocketsCleanup THROW ; time&date_server