Скачиваний:
56
Добавлен:
08.01.2014
Размер:
2.6 Mб
Скачать

10.6. Программирование в режиме пересылок udp-дейтаграмм

Перепишем теперь пример, используя модель дейтаграмм. Основное отличие будет заключаться в том, что дейтаграммы (UDP-пакеты), передаваемые между клиентом и сервером, могут достигать точки назначения в произвольном порядке. К тому же, как уже упоминалось, протокол UDP не гарантирует доставку пакетов. При работе с UDP-сокетами процесс клиента должен также сначала создать сокет и связать с ним свой локальный адрес при помощи вызова bind. После этого процесс сможет использовать этот сокет для посылки и приема UDP-пакетов. Чтобы послать сообщение, процесс должен знать адрес назначения, который может быть как конкретным адресом, так и шаблоном, называемым «широковещательным адресом» и обозначающим сразу несколько компьютеров.

10.6.1. Прием и передача udp-сообщений

Для сокетов UDP есть два новых системных вызова – sendto и recvfrom. Параметр sockfd в обоих вызовах задает связанный с локальным адресом сокет, через который принимаются и передаются пакеты.

Описание

uses stdio;

function recvfrom(sockfd:longint; var message; length, flags:longint;

var send_addr:tsockaddr; var add_len:longint):longint;

function sendto(sockfd:longint; var message; length, flags:longint;

var dest_addr:tsockaddr; dest_len:longint):longint;

Если параметр send_addr равен nil, то вызов recvfrom работает точно так же, как и вызов recv. Параметр message указывает на буфер, в который помещается принимаемая дейтаграмма, а параметр length задает число байтов, которые должны быть считаны в буфер. Параметр flags принимает те же самые значения, что и в вызове recv. Два последних параметра помогают установить двустороннюю связь с помощью UDP-сокета. В структуру send_addr будет помещена информация об адресе и порте, откуда пришел прочитанный пакет. Это позволяет принимающему процессу направить ответ пославшему пакет процессу. Последний параметр является указателем на целочисленную переменную типа longint, в которую помещается длина записанного в структуру send_addr адреса.

Вызов sendto противоположен вызову recvfrom. В этом вызове параметр dest_addr задает адрес узла сети и порт, куда должно быть передано сообщение, а параметр dest_len определяет длину адреса.

Адаптируем пример для модели дейтаграммных посылок.

(* Сервер *)

uses sockets,linux,stdio;

const

SIZE=sizeof(tinetsockaddr);

(* Локальный серверный порт *)

server:tinetsockaddr = (family:AF_INET; port:7000; addr:INADDR_ANY);

client_len:longint=SIZE;

var

sockfd:longint;

c:char;

(* Структура, которая будет содержать адрес процесса 2 *)

client:tinetsockaddr;

begin

(* Установить абонентскую точку сокета *)

sockfd := socket (AF_INET, SOCK_DGRAM, 0);

if sockfd = -1 then

begin

perror ('Ошибка вызова socket');

halt (1);

end;

(* Связать локальный адрес с абонентской точкой *)

if not bind (sockfd, server, SIZE) then

begin

perror ('Ошибка вызова bind');

halt (1);

end;

(* Бесконечный цикл ожидания сообщений *)

while true do

begin

(* Принимает сообщение и записывает адрес клиента *)

if recvfrom (sockfd, c, 1, 0, tsockaddr(client), client_len) = -1 then

begin

perror ('Сервер: ошибка при приеме');

continue;

end;

c := upcase (c);

(* Посылает сообщение обратно *)

if sendto (sockfd, c, 1, 0, tsockaddr(client), client_len) = -1 then

begin

perror ('Сервер: ошибка при передаче');

continue;

end;

end;

end.

Новый текст клиента:

(* Клиентский процесс *)

uses sockets,stdio,linux;

const

SIZE=sizeof(tinetsockaddr);

(* Локальный порт на клиенте *)

client:tinetsockaddr = (family:AF_INET; port:INADDR_ANY; addr:INADDR_ANY);

(* Адрес удаленного сервера *)

server:tinetsockaddr = (family:AF_INET; port:7000);

var

sockfd:longint;

c:char;

begin

(* Преобразовать и записать IP адрес *)

server.addr := inet_addr ('127.0.0.1');

(* Установить абонентскую точку сокета *)

sockfd := socket (AF_INET, SOCK_DGRAM, 0);

if sockfd = -1 then

begin

perror ('Ошибка вызова socket');

halt (1);

end;

(* Связать локальный адрес с абонентской точкой сокета. *)

if not bind (sockfd, client, SIZE) then

begin

perror ('Ошибка вызова bind');

halt (1);

end;

(* Считать символ с клавиатуры *)

while fdread (0, c, 1) <> 0 do

begin

(* Передать символ серверу *)

if sendto (sockfd, c, 1, 0, tsockaddr(server), SIZE) = -1 then

begin

perror ('Клиент: ошибка передачи');

continue;

end;

(* Принять вернувшееся сообщение *)

if recv (sockfd, c, 1, 0) = -1 then

begin

perror ('Клиент: ошибка приема');

continue;

end;

fdwrite (1, c, 1);

end;

end.

Упражнение 10.4. Запустите сервер и несколько клиентов. Как сервер определяет, от какого клиента он принимает сообщение?

Соседние файлы в папке Полищук, Семериков. Системное программирование в UNIX средствами Free Pascal