Другая сторона подтверждает получение уведомления вызовом функции t_rcvrel(3N) . Однако поскольку получение такого уведомления носит асинхронный характер, процесс должен каким-то образом узнать, что запрос поступил. Такой индикацией является завершение с ошибкой попытки получения данных от удаленного узла, например, с помощью функции t_rcv(3N) . В этом случае вызов функции t_rcv(3N) завершится с ошибкой TLOOK
.
Эта ошибка свидетельствует, что произошло событие, связанное с коммуникационным узлом, анализ которого позволяет получить дополнительную информацию о причине неудачи. Текущее событие может быть получено с помощью функции t_look(3N) :
#include
int t_look(int fildes);
Функция возвращает идентификатор, соответствующий одному из событий, перечисленных в табл. 6.6.
Таблица 6.6. События, связанные с коммуникационным узлом
Событие |
Значение |
T_CONNECT |
Узлом получено подтверждение создания соединения |
T_DISCONNECT |
Узлом получен запрос на разрыв соединения |
T_DATA |
Узлом получены данные |
T_EXDATA |
Узлом получены экстренные данные |
T_LISTEN |
Узлом получен запрос на установление соединения |
T_ORDREL |
Узлом получен запрос на корректное прекращение связи |
T_ERROR |
Свидетельствует о фатальной ошибке |
T_UDERR |
Свидетельствует об ошибке датаграммы |
Если в рассматриваемом случае событием, связанным с ошибкой t_rcv(3N) , является T_ORDREL
, это означает, что удаленный узел завершил передачу данных и более не нуждается в соединении. Если узел, получивший запрос на прекращение связи, не возражает против полного прекращения сеанса, он вызывает функцию t_sndrel(3N) . Впрочем, при необходимости, коммуникационный узел может продолжить передачу данных. Единственное, отчего ему следует воздержаться, это от попытки получения данных, или, другими словами, от вызова t_rcv(3N) , поскольку в этом случае выполнение процесса будет навсегда заблокировано, т.к. данные от удаленного узла поступать не будут.
Проиллюстрируем описанную процедуру фрагментом программы, обрабатывающей корректное прекращение связи:
while (t_rcv(fd) != -1) {
/* Выполняем обработку принятых данных */
...
}
if (t_errno == T_LOOK && t_look(fd) == T_ORDREL) {
/* Значит, получен запрос на корректное прекращение связи.
Мы согласны на завершение сеанса, поэтому также корректно
завершаем связь */
t_rcvrel(fd);
t_sndrel(fd);
exit(0);
} else {
t_error("Ошибка получения данных (t_rcv)");
exit(1);
}
Программный интерфейс высокого уровня.
В предыдущих разделах рассматривался программный интерфейс достаточно низкого уровня — по существу программа взаимодействовала непосредственно с транспортным протоколом, самостоятельно реализуя некоторый протокол верхнего уровня при обмене данными. В приведенных примерах легко заметить, что значительная часть кода этих программ посвящена созданию коммуникационных узлов, установлению и завершению связи.
С точки зрения разработчика программного обеспечения, более перспективным является подход, когда используется прикладной программный интерфейс более высокого уровня, изолирующий программу от специфики сетевого взаимодействия. В данном разделе мы рассмотрим один из таких подходов, на базе которого, в частности, разработана файловая система NFS, получивший название удаленный вызов процедур (Remote Procedure Call, RPC).
Использование подпрограмм в программе — традиционный способ структурировать задачу, сделать ее более ясной. Наиболее часто используемые подпрограммы собираются в библиотеки, где могут использоваться различными программами. В данном случае речь идет о локальном (местном) вызове, т.е. и вызывающий, и вызываемый объекты работают в рамках одной программы на одном компьютере.
В случае удаленного вызова процесс, выполняющийся на одном компьютере, запускает процесс на удаленном компьютере (т. е. фактически запускает код процедуры на удаленном компьютере). Очевидно, что удаленный вызов процедуры существенным образом отличается от традиционного локального, однако с точки зрения программиста такие отличия практически отсутствуют, т.е. архитектура удаленного вызова процедуры позволяет сымитировать вызов локальной.
Читать дальше