Взаимодействие с прикладными процессами
Рассмотренный ранее программный интерфейс TLI полностью реализует функциональность TPI. Легко заметить соответствие между отдельными функциями TLI и примитивами TPI, приведенными в табл. 6.10. Схема вызова функций TPI и обмена соответствующими примитивами TPI между клиентом и сервером для типичного TCP-сеанса приведена на рис. 6.32.
Рис. 6.32. Функции TLI и примитивы TPI
Программный интерфейс потоков был рассмотрен в главе 5 при обсуждении подсистемы STREAMS. Основными функциями, обеспечивающими передачу и получение сообщений, являются системные вызовы putmsg(2) и getmsg(2). Таким образом, большинство функций TLI, составляющих программный интерфейс доступа прикладных процессов к транспортным протоколам, являются удобной оболочкой (реализованной в виде библиотеки, например, libnsl.so) более фундаментальным системным вызовам putmsg(2) и getmsg(2).
В качестве примера рассмотрим функцию t_connect(3N). Ее реализация может иметь следующий вид:
int t_connect(int fd, struct t_call *sndcall,
struct t_call *recvcall) {
struct T_conn_req *connreq;
struct T_conn_con* conncon;
struct T_ok_ack *okack;
struct T_error_ack *errack;
struct strbuf connect, ack, confirm, m_data;
struct netbuf addr, opt, udata;
char *buf;
int flags;
...
/* Сохраним адреса буферов netbuf запроса sndcall */
addr = sndcall->addr; opt = sndcall->opt;
udata = sndcall->udata;
/* Заполним поля структуры strbuf для формирования
управляющей части (блок M_PROTO) сообщения T_CONN_REQ */
connect.len =
sizeof(struct T_conn_req) + addr.len + opt.len;
connect.maxlen =
sizeof(struct Т_conn_req) + addr.maxlen + opt.maxlen;
buf = (char*)malloc(connect.maxlen);
connect.buf = buf;
/* Заполним поля заголовка блока M_PROTO сообщения T_CONN_REQ в
соответствии с форматом структуры T_conn_req */
connreq = (struct T_conn_req*)buf;
connreq->PRIM_type = T_CONN_REQ;
connreq->DEST_length = addr.len;
connreq->DEST_offset = sizeof (struct T_conn_req);
buf += sizeof(struct T_conn_req);
memcpy(buf, addr.buf, addr.len);
connreq->OPT_length = opt.len;
connreq->OPT_offset = connreq->DEST_offset + opt.len;
buf += addr.len;
memcpy(buf, opt.buf, opt.len);
/* Заполним поля структуры strbuf для формирования блока данных
(блок M_DATA) */
m_data.len = udata.len;
m_data.maxlen = udata.maxlen;
m_data.buf = udata.buf;
/* Отправим запрос Т_CONN_REQ поставщику транспортных услуг
по потоку fd */
putmsg(fd, &connect, &m_data, 0);
/* Подготовимся к приему подтверждения. Выделим максимальный
размер для получения негативного подтверждения, поскольку
примитив T_ERROR_ACK занимает больше места */
ack.len = ack.maxlen = sizeof(struct T_error_ack);
ack.buf = udata.buf;
/* Подтверждение является приоритетным, поэтому установим флаг
RS_HIPRI. До получения подтверждения не предпринимаем
никаких действий */
flags = RS_HIPRI;
getmsg(fd, &ack, (struct strbuf*)0, &flags);
free(connect.buf);
okack = (struct T_ok_ack*)ack.buf;
/* Проверим получено ли положительное или
негативное подтверждение */
if (okack->PRIM_type == T_OK_ACK) {
/* Если подтверждение положительное, подготовимся к получению
согласия удаленного пользователя на установление связи
(примитив T_CONN_CON) */
free(ack.buf);
if (recvcall != NULL) {
addr = recvcall->addr;
opt = recvcall->opt;
udata = recvcall->udata;
confirm.len = sizeof(struct T_conn_con) + addr.len + opt.len;
confirm.maxlen =
sizeof(struct T_conn_con) + addr.maxlen + opt.maxlen;
buf = (char*)malloc(confirm.maxlen);
confirm.buf = buf;
m_data.len = udata.len;
m_data.maxlen = udata.maxlen;
m_data.buf = udata.buf;
/* Получим примитив T_CONN_CON */
getmsg(fd, &confirm, &m_data, &flags);
free(buf);
conncon = (struct T_conn_con*)confirm.buf;