- •РАБОТА С WINSOCKET
- •ФУНКЦИИ ЛОКАЛЬНОГО УПРАВЛЕНИЯ
- •Создание socket'а
- •Связывание socket'а
- •ФУНКЦИИ УСТАНОВЛЕНИЯ СВЯЗИ
- •Ожидание установления связи
- •Запрос на установление соединения
- •Прием запроса на установление связи
- •Формирование адреса узла сети
- •ФУНКЦИИ ОБМЕНА ДАННЫМИ
- •Посылка данных
- •Получение данных
- •ФУНКЦИИ ЗАКРЫТИЯ СВЯЗИ
- •Системный вызов close
- •Сброс буферизованных данных
- •ПРИМЕР ИСПОЛЬЗОВАНИЯ SOCKET-ИНТЕРФЕЙСА
- •Программа-сервер
- •Программа-клиент
Лекция 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++ |