Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
VC Лекции PDF / VC 18 Лек Работа в сети.pdf
Скачиваний:
43
Добавлен:
15.02.2015
Размер:
315.03 Кб
Скачать

Лекция 18

Таймер

5

flags

модифицирует исполнение системного вызова recv ( ). При нулевом значении этого ар-

 

гумента вызов recv ( ) полностью аналогичен системному вызову read ( ).

 

При успешном завершении recv ( ) возвращает количество принятых в область, указанную аргументом buf, байт данных. Если канал данных, определяемый дескриптором s, оказывается "пустым", то recv ( ) переводит программу в состояние ожидания до момента появления в нем данных.

ФУНКЦИИ ЗАКРЫТИЯ СВЯЗИ

Для закрытия связи с партнером по сетевому взаимодействию используются системные вызовы close ( ) и shutdown ( ).

Системный вызов close

Для закрытия ранее созданного socket'а используется обычный системный вызов close ( ), применяемый в ОС UNIX для закрытия ранее открытых файлов и имеющий следующий вид:

где s

int close ( int s) ;

дескриптор ранее созданного socket'а.

Однако в режиме с установлением логического соединения (обеспечивающем, как правило, надежную доставку данных) внутрисистемные механизмы обмена будут пытаться передать/принять данные, оставшиеся в канале передачи на момент закрытия socket'а. На это может потребоваться значительный интервал времени, неприемлемый для некоторых приложений. В такой ситуации необходимо использовать описываемый далее системный вызов shutdown ( ).

Сброс буферизованных данных

Для "экстренного" закрытия связи с партнером (путем "сброса" еще не переданных данных) используется системный вызов shutdown ( ), выполняемый перед close ( ) и имеющий следующий вид

int shutdown ( int s , int how ) ;

где

 

s

дескриптор ранее созданного socket'а.

how

задает действия, выполняемые при очистке системных буферов socket'а:

0

сбросить и далее не принимать данные для чтения из socket'а;

1

сбросить и далее не отправлять данные для посылки через socket;

2

сбросить все данные, передаваемые через socket в любом направлении.

ПРИМЕР ИСПОЛЬЗОВАНИЯ SOCKET-ИНТЕРФЕЙСА

В данном разделе рассматривается использование socket-интерфейса в режиме взаимодействия с установлением логического соединения на очень простом примере взаимодействия двух программ (сервера и клиента), функционирующих на разных узлах сети TCP/IP.

Содержательная часть программ примитивна:

сервер, приняв запрос на соединение, передает клиенту вопрос "Who are you?";

клиент, получив вопрос, выводит его в стандартный вывод и направляет серверу ответ "I am your client" и завершает на этом свою работу;

сервер выводит в стандартный вывод ответ клиента, закрывает с ним связь и переходит в состояние ожидания следующего запроса к нему.

Предлагаемые ниже тексты программ предназначены только для иллюстрации логики взаимодействия программ через сеть, поэтому в них отсутствуют такие атрибуты программ, предназначенных для практического применения, как обработка кодов возврата системных вызовов и функций, анализ кодов ошибок в глобальной переменной errno, реакция на асинхронные события и т.п.

Программа-сервер

Текст программы-сервера на языке программирования С выглядит следующим образом:

#include <sys/types.h>

// подключает заголовочные файлы,

#include <sys/socket.h>

// содержащие определения всех необходимых

#include <netinet/in.h>

// структур данных и символических констант

#include <netdb.h> #include <memory.h>

Выжол Ю.А. Программирование на Visual C++

Лекция 18

 

Таймер

6

#define SRV_PORT

1234

// номер порта сервера.

 

#define BUF_SIZE

64

// Должен быть известен и программе-клиенту

 

// размер буфера, используемого для размещения

 

 

 

// принимаемых от клиента данных

 

#define TXT_QUEST

”Who are you?\n”

// текст вопроса клиенту

void main ( )

 

 

{

 

 

int s , s_new ; int from_len ;

char buf [ BUF_SIZE ] ;

struct sockaddr_in sin , from_sin ;

//открывает socket для организации режима взаимодействия с установлением логического

//соединения (SOCK_STREAM) в сети TCP/IP (AF_INET),

//при выборе протокола транспортного уровня используется протокол "по умолчанию" (0) s = socket ( AF_INET , SOCK_STREAM , 0 ) ;

memset ((char*)&sin , '\0' , sizeof ( sin )) ;

// обнуляет структуру данных sin

//заполняет поля структуры данных sin. sin.sin_family = AF_INET ; sin.sin_addr.s_addr = INADDR_ANY ;

//Использование константы INADDR_ANY упрощает текст программы,

//избавляя от необходимости вызова функцию gethostbyname ( )

//для получения адреса локального узла, на котором запускается сервер. sin.sin_port = SRV_PORT ;

//привязывает socket, задаваемый дескриптором s,

//к порту с номером SRV_PORT на локальном узле.

bind ( s , ( struct sockaddr *)&sin , sizeof ( sin )) ;

//вызов функции bind ( ) завершится успешно

//если узел не использует другая программа

listen ( s , 3 ) ;

// организует очередь на три входящих к серверу запроса на соединение

while ( 1 )

// начало бесконечного цикла обслуживания запросов от клиентов

{

 

 

from_len = sizeof ( from_sin ) ;

 

s_new = accept ( s , &from_sin , &from_len ) ;

// приём запроса на установление связи

//выполнение программы приостанавливается на неопределенное время,

//если очередь запросов к серверу на установление связи оказывается пуста.

//При появлении запроса на установление связи функция accept ( ) возвращает

//в переменную s_new дескриптор socket'а для обмена информацией с клиентом.

 

write ( s_new , TXT_QUEST , sizeof ( TXT_QUEST )) ;

// сервер отправляет клиенту вопрос

 

from_len = read ( s_new , buf , BUF_SIZE ) ;

 

// сервер читает ответ клиента

 

write ( 1 , buf , from_len ) ;

// направляет ответ в стандартный вывод,

 

shutdown ( s_new , 0 ) ;

// имеющий дескриптор файла номер 1

 

// очистка системных буферов socket'а

 

close ( s_new ) ;

// закрывает socket, использованный для

}

 

// обмена данными с очередным клиентом

// конец бесконечного цикла обслуживания запросов от клиентов

}

Данная программа (как и большинство реальных программ-серверов) самостоятельно своей работы не завершает, находясь в бесконечном цикле обработки запросов клиентов. Ее выполнение может быть прервано только извне путем посылки ей сигналов (прерываний) завершения. Правильно разработанная программа-сервер должна обрабатывать такие сигналы, корректно завершая работу (закрывая с помощью функции close ( ) socket с дескриптором s).

Программа-клиент

Текст программы-клиента на языке программирования С выглядит следующим образом:

#include <sys/types.h>

// подключает заголовочные файлы,

#include <sys/socket.h>

// содержащие определения всех необходимых

#include <netinet/in.h>

// структур данных и символических констант

#include <netdb.h>

 

#include <memory.h>

 

#define SRV_HOST ”delta”

// имя удаленного узла, на котором

Выжол Ю.А.

Программирование на Visual C++

Лекция 18

 

Таймер

7

#define SRV_PORT

1234

// функционирует программа-сервер

 

// номер порта, к которому привязан socket сервера

 

#define CLNT_PORT

1235

// номера порта клиента

 

#define BUF_SIZE

64

// размер буфера, используемого для размещения

 

 

 

// принимаемых от сервера данных

 

#define TXT_ANSW

”I am your client\n”

// текст ответа серверу

void main ( )

 

 

{

 

 

int s ;

int from_len ;

char buf [ BUF_SIZE ] ; struct hostent* hp ;

struct sockaddr_in clnt_sin , srv_sin ;

s = socket (AF_INET , SOCK_STREAM , 0 ) ;

//открывает socket для организации режима взаимодействия с установлением

//логического соединения (SOCK_STREAM) в сети TCP/IP (AF_INET),

//при выборе протокола транспортного уровня используется протокол "по умолчанию" (0)

memset (( char*)&clnt_sin , '\0' , sizeof ( clnt_sin )) ;

// обнуляет структуру данных clnt_sin

// заполняет поля структуры данных sin.

 

clnt_sin.sin_family = AF_INET ; clnt_sin.sin_addr.s_addr = INADDR_ANY ; clnt_sin.sin_port = CLNT_PORT ;

bind ( s , ( struct sockaddr*)&clnt_sin , sizeof ( clnt_sin )) ; // создает socket, привязанный

// к порту на локальном узле memset (( char*)&srv_sin , '\0', sizeof ( srv_sin )) ; // обнуляет структуру данных srv_sin

hp = gethostbyname ( SRV_HOST ) ; // транслирует символическое имя удаленного узла ("delta"),

//на котором должен функционировать сервер,

//в адрес этого узла, размещенный в структуре типа hostent

srv_sin.sin_family = AF_INET ;

memcpy (( char*)&srv_sin.sin_addr , hp->h_addr,hp->h_length ) ;

//адрес удаленного узла копируется из структуры типа hostent

//в соответствующее поле структуры srv_sin

srv_sin.sin_port = SRV_PORT ;

 

connect ( s , &srv_sin , sizeof ( srv_sin )) ;

// идентифицирует программу-сервер

from_len = recv ( s , buf , BUF_SIZE , 0 ) ;

// принимает данные от сервера

write ( 1, buf , from_len) ;

// направляет данные в стандартный вывод

send ( s , TXT_ANSW , sizeof ( TXT_ANSW ) , 0 ) ; // посылает ответ серверу

close ( s ) ;

// закрывает socket

exit ( 0 ) ;

 

}

Выжол Ю.А.

Программирование на Visual C++