TCP/IP - это два основных сетевых пpотокола Internet. Часто это название используют и для обозначения сетей, pаботающих на их основе. Пpотокол IP (Internet Protocol - IP v4) обеспечивает маpшpутизацию (доставку по адpесу) сетевых пакетов. Пpотокол TCP (Transfer Control Protocol) обеспечивает установление надежного соединения между двумя машинами и собственно пеpедачу данных, контpолиpуя оптимальный pазмеp пакета пеpедаваемых данных и осуществляя пеpепосылку в случае сбоя. Число одновpеменно устанавливаемых соединений между абонентами сети не огpаничивается, т. е. любая машина может в некоторый промежуток времени обмениваться данными с любым количеством дpугих машин по одной физической линии.
Дpугое важное пpеимущество сети с протоколами TCP/IP состоит в том, что по нему могут быть объединены машины с pазной аpхитектуpой и разными опеpационными системами, напpимеp Unix, VAX VMS, MacOS, MS-DOS, MS Windows и т.д. Пpичем машины одной системы пpи помощи сетевой файловой системы NFS (Net File System) могут подключать к себе диски с файловой системой совсем дpугой ОС и опеpиpовать "чужими" файлами как своими.
Протоколы TCP/IP (Transmission Control Protocol/Internet Protocol) являются базовыми транспортным и сетевым протоколами в OS UNIX. В заголовке TCP/IP пакета указывается:
IP-адрес отправителяIP-адрес получателяНомер порта (Фактически - номер прикладной программы, которой этот пакет предназначен)
Пакеты TCP/IP имеют уникальную особенность добраться до адресата, пройдя сквозь разнородные в том числе и локальные сети, используя разнообразные физические носители.Маршрутизацию IP-пакета (переброску его в требуемую сеть) осуществляют на добровольных началах компьютеры, входящие в TCP/IP сеть.
Протокол IP - это протокол, описывающий формат пакета данных, передаваемого по сети.
Следующий простой пример можетн прояснить, каким образом происходит передача данных и передача данных. Когда Вы получаете телеграмму, весь текст в ней (и адрес, и сообщение) написан на ленте подряд, но есть правила, позволяющие понять, где тут адрес, а где сообщение. Аналогично, пакет в компьютерной сети представляет собой поток битов, а протокол IP определяет, где адрес и прочая служебная информация, а где сами передаваемые данные. Таким образом, протокол IP в эталонной модели ISO/OSI является протоколомсетевого (3) уровня.
Протокол TCP - это протокол следующего уровня, предназначеный для контроля передачи и целостности передаваемой информации.
Когда Вы не расслышали, что сказал Вам собеседник в телефонном разговоре, Вы просите его повторить сказанное. Приблизительно этим занимается и протокол TCP применительно к компьютерным сетям. Компьютеры обмениваются пакетами протокола IP, контролируют их передачу по протоколу TCP и, объединяясь в глобальную сеть, образуют Интернет. Протокол TCP является протоколом транспортного (4) уровня
21 создание канала связи между клиентом и сервером
Клиентом и сервером называются процессы, работающие одновременно на одном или разных компьютерах (объединенных в сеть) и взаимодействующие между собой определенным образом.
Клиент посылает серверу запрос на получение данных или выполнение какой-либо работы. Сервер, получив от клиента запрос, выполняет соответствующие действия и посылает клиенту ответ. Процесс передачи запроса серверу называется транзакцией.
Строго говоря, транзакция – это совокупность трех действий: посылки запроса, выполнение запроса, приема ответа.
Транзакция называется завершенной, если выполнены все три действия. Если же на одном из трех этапов произошел сбой, транзакция остается незавершенной. После восстановления незавершенная транзакция откатывается, что необходимо в ряде случаев для сохранения целостности данных (разумеется, за правильное выполнение отката транзакции отвечает сервер).
В системе, имеющей архитектуру “клиент – сервер”, может быть несколько клиентов, работающих с одним сервером, или несколько клиентов, работающих одновременно с несколькими серверами.
Приложения, использующие технологию динамического обмена данными DDE, выступают как клиенты или серверы (или одновременно как клиенты и серверы). При этом взаимодействие между ними – не что иное, как транзакция. Библиотека ddeml.dll позволяет создавать системы, имеющие различные топологии.
2.25.2. Инициализация и создание канала связи
Рассмотрим процесс создания канала связи между двумя одновременно работающими приложениями. Пусть одно приложение будет клиентом, а другое – сервером.
В процессе инициализации сервер должен выполнить следующие действия:
· зарегистрировать себя в библиотеке DDEML;
· зарегистрировать предоставляемый сервис, которым сможет воспользоваться приложение-клиент.
Клиент должен сделать следующее:
· зарегистрировать себя в библиотеке DDEML;
· создать канал связи с сервером, указав необходимый сервис.
Рассмотрим эти действия подробнее.
Регистрация в библиотеке DDEML.Если приложение собирается использовать DDEML, оно должно зарегистрировать себя в библиотеке DDEML, вызвав специально предназначенную для этого функцию с именем DdeInitialize.
Функция DdeInitialize используется в процессе инициализации и серверов, и клиентов. Сама по себе она не создает никаких каналов передачи данных между приложениями, однако процедура регистрации должна быть проведена до вызова любых других функций, имеющих отношение к DDEML.
Если приложение больше не собирается работать с библиотекой DDEML, оно должно вызвать функцию DdeUninitialize.
Регистрация сервиса.Следующий этап в инициализации сервера DDEML заключается в регистрации предоставляемого им сервиса.
Библиотека DDEML использует трехступенчатую схему адресации данных, передаваемых по каналу связи: сервис (service), раздел (topic) и элемент данных (data item). Приложение задает элементы адреса в виде текстовых строк размером не более 356 байт.
Сервер DDEML может предоставлять сервис одного или нескольких видов. Как правило, один сервер предоставляет только один сервис, причем текстовая строка, идентифицирующая сервис, часто совпадает с именем приложения, но можно выбрать любую другую строку.
Второй элемент адреса – раздел. В рамках одного сервиса можно определить несколько разделов. Когда клиент DDEML создает канал с сервером, он указывает сервис и раздел. Раздел объединяет группу элементов данных или выполняемых функций.
Канал DDEML служит для передачи блоков данных. В рамках одного раздела сервер может обмениваться с клиентом разными блоками данных, каждый из которых идентифицируется при передаче именем элемента данных. В процессе создания канала связи не требуется указывать элементы данных.
Регистрация сервиса выполняется сервером DDEML обычно сразу после вызова функции DdeInitialize и происходит в два этапа.
На первом этапе текстовая строка имени сервиса сохраняется в специальной системной таблице, для чего вызывается функция DdeCreateStringHandle.
Идентификатор текстовой строки, возвращенный функцией DdeCreateStringHandle и соответствующий регистрируемому сервису, следует передать функции DdeNameService.
Перед завершением работы сервер DDEML должен отменить весь зарегистированный им ранее сервис, вызвав функцию DdeInitialize с соответствующим флагом.
Одновременно с регистрацией сервиса сервер обычно создает идентификаторы текстовых строк, содержащих имена используемых разделов и элементов данных. Для этого вызывается все та же функция DdeCreateStringHandle.
Отметим, что регистрацию сервиса выполняет только сервер. Что же касается создания идентификаторов текстовых строк функцией DdeCreateStringHandle, то эта операция выполняется как сервером, так и клиентом. Полученные идентификаторы используются при создании канала и в процессе передачи данных.
Если идентификатор созданной строки используется в функции обратного вызова, то за освобождения ресурсов, связанных с текстовой строкой, отвечает система DDEML. В противном случае необходимо использовать функцию DdeFreeStringHandle.
Функция обратного вызова DDEML.Когда клиент или сервер регистрируется в библиотеке DDEML, он указывает адрес переходника, созданного для функции обратного вызова. Эта функция предназначена для обработки событий, возникающих в процессе создания каналов связи и передачи данных.
Простейшая функция обратного вызова сервера DDEML имеет вид
HDDEDATA EXPENTRY _export DdeServer(wType, wFmt, hConv ,hsz1, hsz2, hData, dwData1, dwData2)
WORD wType; // код транзакции
WORD wFmt; // формат данных
HCONV hConv; // идентификатор канала
HSZ hsz1; // первый идентификатор строки
HSZ hsz2; // второй идентификатор строки
HDDEDATA hData; // идентификатор глобальной области памяти
DWORD dwData1; // первое дополнительное двойное слово
DWORD dwData2; // второе дополнительное двойное слово
{
switch(wType)
{
case XTYP_CONNECT:
// создание канала передачи данных
. . .
return((HDDEDATA)TRUE);
case XTYP_CONNECT_CONFIRM:
// подтверждение создание канала
. . .
break;
case XTYP_DISCONNECT:
// завершение работы канала
. . .
break;
case XTYP_REQUEST: // запрос данных от сервера
. . .
return(hData);
case XTYP_EXECUTE:
// запрос на выполнение команды
. . .
break;
case XTYP_POKE: // передача данных серверу
. . .
return((HDDEDATA)DDE_FACK);
case XTYP_ERROR: // ошибка
. . .
break;
}
return((HDDEDATA)NULL);
}
Функция обратного вызова для клиента DDEML выглядит точно также, отличаясь только составом обрабатываемых транзакций
HDDEDATA EXPENTRY _export DdeServer(wType, wFmt, hConv ,hsz1, hsz2, hData, dwData1, dwData2)
WORD wType; // код транзакции
WORD wFmt; // формат данных
HCONV hConv; // идентификатор канала
HSZ hsz1; // первый идентификатор строки
HSZ hsz2; // второй идентификатор строки
HDDEDATA hData; // идентификатор глобальной области памяти
DWORD dwData1; // первое дополнительное двойное слово
DWORD dwData2; // второе дополнительное двойное слово
{
switch(wType)
{
case XTYP_DISCONNECT: // завершение работы канала
return((HDDEDATA)NULL);
case XTYP_ERROR: // ошибка
break;
case XTYP_XACT_COMPLETE:
// завершение выполнения команды
break;
}
return((HDDEDATA)NULL);
}
Создание и уничтожение канала связи.Последнее, что нужно сделать перед началом передачи данных, – создать канал связи. Канал связи между клиентом и сервером создается всегда по инициативе клиента. После регистрации в библиотеке DDEML клиент вызывает функцию DdeConnect, создающую канал связи.
Рассмотрим, что происходит при создании канала при помощи функции DdeConnect.
Прежде всего библиотека DDEML посылает транзакцию с кодом XTYP_CONNECT всем активным серверам, которые зарегистрировали сервис, указанный в параметрах функции DdeConnect. Обработчик этого сообщения должен проверить сервис и раздел, если сервер их поддерживает, то можно создавать канал. В таком случае функция обратного вызова должна вернуть TRUE, в противном – FALSE.
В случае успешного создания канала сервер получает от системы DDEML транзакцию с кодом XTYPE_CONNECT_CONFIRM. При обработке этой транзакции сервер может сохранить идентификатор созданного канала для дальнейшего использования.
Если канал связи больше не нужен, клиент или сервер может уничтожить его, вызвав функцию DdeDisconnect. Партнер приложения, вызвавшего эту функцию, получит транзакцию XTYPE_DISCONNECT. Здесь можно освободить заказанные ранее ресурсы для работы с каналом связи.
22 передача и прием данных
int info = pvm_send( int tid, int msgtag)
call pvmfsend( tid, msgtag, info)
int info = pvm_mcast( int *tids, int ntask, int msgtag)
call pvmfmcast( ntask, tids, msgtag, info)
Подпрограмма pvm_send() помечает сообщение целочисленным идентификатором msgtag и передает его непосредственно процессу TID.
Подпрограмма pvm_mcast() помечает сообщение целочисленным идентификатором msgtag и широковещательно передает это сообщение всем задачам, указанным в целочисленном массиве tids (исключая себя). Массив tids имеет длину ntask.
int info = pvm_psend( int tid, int msgtag, void *vp,
int cnt, int type)
call pvmfpsend( tid, msgtag, xp, cnt, type, info)
Подпрограмма pvm_psend() упаковывает и посылает массив данных указанного типа задаче, идентифицированной TID. Предопределенные типы данных на Фортране такие же, как и для pvmfpack(). В языке C аргумент type может иметь любое из следующих значений:
PVM_STR
PVM_FLOAT
PVM_BYTE
PVM_CPLX
PVM_SHORT
PVM_DOUBLE
PVM_INT
PVM_DCPLX
PVM_LONG
PVM_UINT
PVM_USHORT
PVM_ULONG
PVM поддерживает несколько методов приема сообщений в задаче. В PVM нет точного соответствия функций, например, применение pvm_send не обязательно требует примененияpvm_recv. Каждая из следующих подпрограмм может быть вызвана для любого из поступающих сообщений вне зависимости от того, как оно было передано (или передано широковещательно).
int bufid = pvm_recv( int tid, int msgtag)
call pvmfrecv( tid, msgtag, bufid)
Эта подпрограмма блокирующего приема будет ожидать до тех пор, пока от задачи с TID не поступит сообщение с меткой msgtag. Значение -1 в msgtid или TID означает ``все задачи'' (специальный символ). После поступления она помещает сообщение в новый создаваемый активный буфер приема. Предыдущий активный буфер приема очищается, если он не был сохранен вызовом pvm_setrbuf().
int bufid = pvm_nrecv(int tid, int msgtag)
call pvmfnrecv( tid, msgtag, bufid)
Если запрашиваемое сообщение не прибыло, то неблокирующий прием pvm_nrecv() при завершении вернет код, равный 0. Эта подпрограмма может вызываться сколько угодно раз для определенного сообщения - с целью проверки его прибытия - в промежутках выполнения работы программы. Если же возможной в данной ситуации работы не осталось, для того же сообщения можно воспользоваться блокирующим приемом pvm_recv(). Если сообщение с меткой msgtag поступило от задачи с TID, pvm_nrecv() помещает это сообщение в новый активный буфер (который она создает) и возвращает идентификатор данного буфера. Предыдущий активный буфер приема очищается, если он не был сохранен вызовомpvm_setrbuf(). Значение -1 в msgtid или TID означает ``все задачи'' (специальный символ).
int bufid = pvm_probe( int tid, int msgtag)
call pvmfprobe( tid, msgtag, bufid)
Если запрашиваемое сообщение не прибыло, то pvm_probe() возвращает bufid, равный 0. В противном случае она возвращает bufid сообщения, но не ``принимает'' его. Эта подпрограмма может вызываться сколько угодно раз для определенного сообщения - с целью проверки его прибытия - в промежутках выполнения работы. Дополнительно может быть вызвана pvmbufinfo() с возвращенным bufid - для получения информации о сообщении перед его непосредственным приемом.
int bufid = pvm_trecv( int tid, int msgtag,
struct timeval *tmout)
call pvmftrecv( tid, msgtag, sec, usec, bufid)
PVM также поддерживает версию приема с тайм-аутом. Рассмотрим случай, при котором сообщение не прибывает никогда (из-за ошибки или сбоя): подпрограмма pvm_recv может заблокироваться навечно. Для избежания такой ситуации пользователь может захотеть ``прекратить'' ожидание после истечения фиксированного временного отрезка. Подпрограммаpvm_trecv() предоставляет пользователю возможность указать период тайм-аута. Если этот период очень велик, то pvm_trecv() действует подобно pvm_recv. Если же период тайм-аута установлен в ноль, то pvm_trecv() действует подобно pvm_nrecv. Так, pvm_trecv ``заполняет пробел'' между функциями блокирующего и неблокирующего приема.
Подпрограмма pvm_bufinfo() возвращает msgtag, TID источника и длину в байтах сообщения, идентифицированного с помощью bufid. Она может применяться для установления метки и источника сообщений, которые были приняты с использованием специальных символов.
Подпрограмма pvm_precv() сочетает в себе функции блокирующего приема и распаковки буфера приема. Она не возвращает bufid. Вместо него она возвращает действительные значения TID, msgtag и cnt.
int info = pvm_precv( int tid, int msgtag, void *vp,
Подпрограмма pvm_recvf() модифицирует контекст работы принимающих функций и может быть использована для ``расширения'' PVM. Используемый по умолчанию контекст приема заключается в соответствии источника и тега сообщения. Он может быть модифицирован для любой определяемой пользователем функции сравнения. Подпрограммы, соответствующей pvm_recvf(), с интерфейсом на Фортране - нет.