Генерация динамических HTML-страниц
"Печать" динамических HTML-данных
Чаще всего код Tibbo BASIC/C, включённый в HTML-страницу, нужен для формирования динамического HTML-содержимого (то есть для отправки браузеру динамически сгенерированных данных). Это делается так же, как отправка данных в обычном режиме данных: сначала методом sock.setdata задаются данные для отправки, затем методом sock.send они передаются на отправку.
Пример HTML-страницы, которая проверяет состояние одного из входов объекта io и выводит это состояние на странице:
<!DOCTYPE HTML public "-//W3C//DTD W3 HTML//EN">
<HTML>
<BODY>
The state of line 0 is:!<br>
<?
'This is a BASIC code fragment. Get the state of the line.
io.num=0
if io.state= LOW then
sock.setdata("LOW")
else
sock.setdata("HIGH")
end if
sock.send
?>
</BODY>
</HTML>
Обратите внимание: sock.num задавать не пришлось - он устанавливается автоматически, когда управление передаётся Tibbo BASIC/C!
Следите, чтобы данные не обрезались!
Приведённый пример выглядит верным, и в данном случае он будет работать нормально: перед местом генерации динамического содержимого статических данных немного, да и самого динамического содержимого тоже немного. Вообще же рассчитывать на то, что в буфере передачи хватит места, когда понадобится положить в него данные, нельзя! Если не быть внимательным, генерируемые динамические данные могут обрезаться!
Чтобы этого избежать, всегда проверяйте, достаточно ли свободного места, прежде чем помещать данные в буфер передачи. Это справедливо и для обычного режима данных, а не только для HTTP-обработки; мы уже затрагивали эту тему в разделе Отправка данных TCP и UDP. Для HTTP отличие в том, что событие on_sock_data_sent нельзя использовать, чтобы вас "уведомили", когда буфер передачи освободится!
Единственное решение для HTTP - ждать в цикле, пока не появится нужный объём места. Естественно, блокировать всё устройство не нужно, поэтому "вежливое" ожидание должно включать оператор doevents:
<?
dim s as string(4)
?>
<!DOCTYPE HTML public "-//W3C//DTD W3 HTML//EN">
<HTML>
<BODY>
The state of line 0 is:!<br>
<?
'This is a BASIC code snippet. Get the state of the line.
io.num=0
if io.state= LOW then
s="LOW"
else
s=HIGH
end if
'and now we wait till the TX buffer has enough free space
while sock.txfree<len(s)
doevents
wend
'OK, so necessary space is now available!
sock.setdata(s)
sock.send
?>
</BODY>
</HTML>
Теперь динамическое содержимое формируется надёжно, и работа не блокируется! По мере накопления опыта с HTML вы увидите, что оператор doevents приходится использовать довольно часто.
Особый случай: doevents и одновременный запрос одного и того же файла
В HTTP вполне возможно, что два компьютера (браузера) запросят один и тот же HTML-файл. Если под HTTP выделено больше одного сокета, оба запроса могут обрабатываться одновременно. Но виртуальная машина TiOS - однопроцессная система, поэтому при обработке динамической части файлов один из запросов окажется первым.
Вполне возможно и так, что при выполнении процедуры Tibbo BASIC/C из первого "экземпляра" файла выполнение доходит до doevents, и начинается обработка второго экземпляра того же файла. Ведь doevents позволяет выполняться другим событиям, в том числе активности в других сокетах.
Если бы той же процедуре Tibbo BASIC/C разрешили выполниться повторно, возникла бы рекурсия: попытка выполнить фрагмент кода, который уже выполняется! Рекурсия не допускается, поэтому виртуальная машина не станет входить в код повторно сразу. Вместо этого второй экземпляр HTML-страницы будет "ждать", пока первый не закончит выполнять эту процедуру. После этого та же процедура выполнится для второго экземпляра. Такое поведение появилось в TiOS V1.2. В более ранних версиях выполнение процедуры для второго экземпляра HTML-страницы просто пропускалось, что неправильно!
Повторим: чтобы это произошло, должны выполняться два условия. HTML-процедура должна использовать оператор doevents, а один и тот же файл (HTML-страница) должен одновременно запрашиваться из нескольких браузеров.
Недопустимые символы
HTTP запрещает использовать некоторые символы: произвольный код из таблицы ASCII отправлять нельзя. Когда вы используете sock.setdata и sock.send, вы "печатаете" данные прямо в браузер. Избегать недопустимых символов и вместо них использовать "%xx" должна ваша программа.