Формирование адреса узла сети

Для получения адреса узла сети TCP/IP по его символическому имени
используется библиотечная функция

#include <netinet/in.h>

#include <netdb.h>

struct hostent *gethostbyname (name)

char *name;

Аргумент name задает адрес последовательности литер, образующих символическое
имя узла сети.

При успешном завершении функция возвращает указатель на структуру hostent,
определенную в include-файле netdb.h и имеющую следующий вид struct hostent {
char *h_name; char **h_aliases; int h_addrtype; int h_lenght; char *h_addr; };

Поле h_name указывает на официальное (основное) имя узла.

Поле h_aliases указывает на список дополнительных имен узла (синонимов), если
они есть.

Поле h_addrtype содержит идентификатор используемого набора протоколов, для
сетей TCP/IP это поле будет иметь значение AF_INET.

Поле h_lenght содержит длину адреса узла.

Поле h_addr указывает на область памяти, содержащую адрес узла в том виде, в
котором его используют системные вызовы и функции socket-интерфейса.

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

Функции обмена данными

В режиме с установлением логического соединения после удачного выполнения
пары взаимосвязанных системных вызовов connect (в клиенте) и accept (в сервере)
становится возможным обмен данными.

Этот обмен может быть реализован обычными системными вызовами read и write,
используемыми для работы с файлами (при этом вместо дескрипторов файлов в них
задаются дескрипторы socket'ов).

Кроме того могут быть дополнительно использованы системные вызовы send и
recv, ориентированные специально на работу с socket'ами.

Примечание. Для обмена данными в режиме без установления логического
соединения используются, как правило, системные вызовы sendtoи recvfrom. Sendto
позволяет специфицировать вместе с передаваемыми данными (составляющими
дейтаграмму) адрес их получателя. Recvfrom одновременно с доставкой данных
получателю информирует его и об адресе отправителя.

Посылка данных

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

#include <sys/types.h>

#include <sys/socket.h>

int send (s, buf, len, flags)

int s;

char *buf;

int len;

int flags;

Аргумент s задает дескриптор socket'а, через который посылаются данные.

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

Аргумент len задает длину (в байтах) передаваемых данных.

Аргумент flags модифицирует исполнение системного вызова send. При нулевом
значении этого аргумента вызов send полностью аналогичен системному вызову
write.

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

Получение данных

Для получения данных от партнера по сетевому взаимодействию используется
системный вызов recv, имеющий следующий вид

#include <sys/types.h>

#include <sys/socket.h>

int recv (s, buf, len, flags)

int s;

char *buf;

int len;

int flags;

Аргумент s задает дескриптор socket'а, через который принимаются данные.

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

Аргумент len задает длину (в байтах) этой области.

Аргумент flags модифицирует исполнение системного вызова recv. При нулевом
значении этого аргумента вызов recv полностью аналогичен системному вызову read.

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

Функции закрытия связи

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

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

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

int close (s)

int s;

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

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

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

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

int shutdown (s, how)

int s;

int how;

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

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

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

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

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

5. Пример использования socket-интерфейса

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

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

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

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

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

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

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

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

program Server;

{$APPTYPE CONSOLE}

{ Для использования winsock необходимо описать этот модуль в uses. }

uses

sysutils,

winsock;

{ дефолтовый порт Socks прокси. Сюда можно вписать любой порт,

превратив наш сканнер, к примеру, в httpd (80 порт) сканнер. }

const

port = 27015;

{ Здесь объявляем переменные }

var

D:WSAData;

S, testSocket:TSocket;

mySocket: TSockAddr;

newSocketAddr: tSockAddrIn;

Address_Size: Integer;

x: char;

begin

writeln('- Client started:' );

writeln;

{ Если WSAStartup() возвращает не нулевое значение, выводим сообщение об ошибке

и выходим из программы}

if WSAStartup($101,D)<>0 then

begin

writeln('error..');

exit;

end;

{ Создаем сокет }

S:=socket(AF_INET,SOCK_STREAM,0);

writeln (S);

{ Если возвращено значение INVALID_SOCKET, выводим сообщение об ошибке }

if S=INVALID_SOCKET then

writeln('socket error');

{ связывание сокета }

mySocket.sin_family:=AF_INET;

mySocket.sin_addr.S_addr:=INADDR_ANY;

mySocket.sin_port := htons(port);

if bind (S, mySocket, sizeof( mySocket ) ) = 0 then

writeln(' socket bound') else

writeln(' socket not bound');

readln;

if listen(S,10) = 0 then

writeln(' listening to the NET') else

writeln(' failed to listen');

Address_Size := sizeof ( newSocketAddr );

writeln (Address_Size);

testSocket := -1;

while testSocket <= 0 do

begin

writeln('_____');

testSocket:=accept(S, @newSocketAddr, @Address_Size);

end;

writeln(' client accepted');

writeln ( recv(testSocket, x, sizeof(x), 0));

writeln (x);

writeln ( send(testSocket, x, sizeof(x), 0));

{ Завершаем работу с сокетами }

WSACleanup;

writeln('- Socket closed -');

readln;

{end; }

end.

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

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

program Client;

{$APPTYPE CONSOLE}

{ Для использования winsock необходимо описать этот модуль в uses. }

uses

sysutils,

winsock;

{ дефолтовый порт Socks прокси. Сюда можно вписать любой порт,

превратив наш сканнер, к примеру, в httpd (80 порт) сканнер. }

const

port = 27015;

{ Здесь объявляем переменные }

var

D:WSAData;

S:TSocket;

A, mySocket:TSockAddr;

begin

writeln('- Client started:' );

writeln;

{ Если WSAStartup() возвращает не нулевое значение, выводим сообщение об ошибке

и выходим из программы}

if WSAStartup($101,D)<>0 then

begin

writeln('error..');

exit;

end;

{ Создаем сокет }

S:=socket(AF_INET,SOCK_STREAM,0);

{ Если возвращено значение INVALID_SOCKET, выводим сообщение об ошибке }

if S=INVALID_SOCKET then

writeln('socket error');

{ связывание сокета }

mySocket.sin_family:=AF_INET;

mySocket.sin_addr.S_addr:=INADDR_ANY;

if bind (S, mySocket, sizeof( mySocket ) ) = 0 then

writeln(' socket bound') else

writeln(' socket not bound');

readln;

{ Определяем сокет для установления соединения }

A.sin_family:=AF_INET;

A.sin_addr.S_addr:=inet_addr(pchar('192.168.0.7'));

{ Определяем порт (задается константой) }

A.sin_port:=htons(port);

{ Пытаемся подконнектиться, если удачно - выводим сообщение, что порт открыт,

в другом случае - сообщение о том, что порт закрыт (или недоступен) }

if connect(S,A,sizeof(A))=0 then

writeln(' port opened') else

writeln(' port closed');

readln;

{end;}

{ Завершаем работу с сокетами }

WSACleanup;

writeln;

writeln('- Socket closed -');

{end; }

end.

По материаллам сайта: www.realcoding.net

Наши рекомендации