Хелпикс

Главная

Контакты

Случайная статья





TCP-сервер. UDP-сервер



TCP-сервер

#подключаем Perl

#! /usr/bin/perl

 

#Подключаем наш компонент для работы с сокетом

use IO:: Socket;

 

$Server_port = 80;

 

#создаемсокет

$server = IO:: Socket:: INET-> new(

LocalPort => $Server_port,

Type   => SOCK_STREAM,

Reuse => 1,

Listen => 10)

or die " Couldn't start TCP-server\n";

 

#пока есть подключение, сделать что-то с этим подключением

while ( $client = $server -> accept() ) {

#клиент и есть подключение, берём от него сообщение и печатаем

$sub = < $client>;

print $sub;

}

 

#закрываемсокет

close($server);

 

Комментарий:

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

 

 

Теперь рассмотрим UDP-сервер:

 

UDP-сервер

#подключаем Perl

#! /usr/bin/perl

 

#Подключаем наш компонент для работы с сокетом

use IO:: Socket;

 

$Server_port = 1000;

 

#создаемсокет

$server = IO:: Socket:: INET-> new(

LocalPort => $Server_port,

Proto  => " udp" )

or die " Couldn't start UDP-server\n";

 

$MAX_TO_READ = 1024;

 

#если общее количество принятой информации не достигло 1024 байта, то

#читаемв $datagram

while ( $server -> recv($datagram, $MAX_TO_READ, $flags) ) {

#печатаем сообщения

print $datagram;

}

 

#закрываем сокет

close($server);

 

Комментарий:

С UDP всё намного проще, мы не устанавливаем никакогосоединения, а просто принимаем сообщения по мере поступленияс помощью функции recv(), где первый параметр само принятоесообщение, второй – целое число, максимальная длина сообщения, ну и параметры. Дальше печатаем сообщение и закрываем сокет.

 

Теперь ты с уверенностью можешь сказать, что ты знаешь, какработать с сокетами в Perl: ). Все программы, написанные выше, будут работать на любой ОС с установленным Perl в базовой комплектации.

 

А теперь… мы поговорим оболее сложных вещах, чем сокеты: )

Давай попробуем собрать пакет с нуля. Но прежде, чем это сделать нам потребуются некоторые элементарные знания по протоколам.

Собирать будем пакет семейства протоколов TCP/IP, так как это самое сложное, а с UDP я думаю, ты разберёшься и самбез проблем по докам (в сети есть полное описание компонента Net:: RawIP).

Я дам только самую необходимую информацию по TCP/IP. Итак, TCP/IP – это не один протокол, это целых два протокола в одном флаконе IP и TCP.

Каждый из них выполняет свою функцию и не лезет в дела другого.

Так IP нужен для того чтобы сообщение дошло по адресу, то есть это иесть адрес в обычном понимании. В заголовке IP самое главное содержатьсяIP-адреса отправителя и получателя, а также контрольная сумма cheksum, гарантирующая целостность всего пакета, но IP не гарантирует, что пакет вообще дойдёт – этим занимается протокол TCP.

Заголовок протоколаTCP содержит информацию обо всём остальном, именно он устанавливаетсоединение и создаёт иллюзию туннеля при соединении. Итак, в заголовкесодержаться информация о порте отправителя и получателя и куча флагов –seq, ack_seq, urg, ack, psh, rst, syn, fin, doff.

Давай обо всём по порядку:

Seq – 32-битное поле - номер последовательности, служит для контроляполучателя отравителя, чтобы понять с тем ли нужным нам пакетом имеемдело – гарантирует правильную последовательность передачи пакетов –задаём в самом начале случайное значение.

Ack_seq – 32-битное поле, содержимое которого косвенно определяетколичество принятых данных, служит опять же для уверенности в том, что всё хорошо – высчитывается нами.

Urg – Флаг, единичное значение которого говорит о том, что мы имеемдело с так называемыми «важными» данными, которые обслуживаются вне очереди что это за данные сказано в urg_ptr.

Ack – флаг, если единичное значение, то TCP-пакет содержит вполе ack_seq верные данные.

Psh – флаг, если единица, то данные, содержащиеся в пакете должныбыть немедленно переданы прикладной программе, для которой ониадресованы.

RST –флаг, устанавливающийся в единицу в TCP-пакете, отправляемомв ответ на получение неверного TCP-пакета. Так же запрос на переустановку TCP-соединения.

SYN – флаг, если единица, значит просим об установлении соединения.

FIN – если единица, значит запрос на закрытие соединения.

Doff – смещение данных – 4-битное поле, содержащие длину TCP-заголовка, нужно для определения начала самих данных – выщипывается автоматомнашим модулем.

Так же в TCP-заголовке есть своя checksum для полного контроля над пакетом.

Вот основные параметры TCPIP заголовков – ведь просто, правда?: )

Теперь рассмотрим процесс установления соединения между получателеми отправителем. Итак, пусть хост A хочет активно слиться в едином экстазес хостом B. Что же ему поможет в его нелёгком деле, а поможет ему флаг SYN, вот схема:

A ---> { SYN = 1, seq = 1000 (random значение 32-бита), ldata (длинна данных) = 0} ------> B

Если всё в порядке и наша дама B узнала хахаля A и он ей

понравился (порт открыт – давай стыковку: )), то идёт пакет:

B ---> SYN = 1 (даёт разрешение на посадку), ACK = 1(вас поняла, даю подтверждение правильности SEQ), ack_seq = 1000 + 1(это чтобы потом тебя милый узнать), seq = 3000 (ещё один random, так же для якоря на нашего хахаля), ldata = 0 ----> A

И чтобы закончить слияние нужно что сделать?

Правильно само действие – закончить, то есть начать: ):

A ---> ACK = 1(всё верно – даю стыковку), ack_seq = 3000 + 1(подтверждение на правильность места, туда ли стыкуемся? ), seq = 1001 (номер последовательности – да, это я милая: )), ldata = 0 ---> B

Всё стыковка прошла нормально – есть контакт! Теперь нужно

сделать самое главное - влить в крошку данные и принять от

неё их, вот так это происходит:

A ----> ACK = 1 (всё нормально, понял вас, продолжаю полёт), seq = 1001, ack_seq = 3001, ldata (длинна данных в сообщение) = 50 ---> B

Обрати внимание на длину данных и в следующей схеме на

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

B ----> ACK = 1(всё впорядке), seq = 3001 (предыдущий номер подтверждения), ack_seq = 1001 + ldata = 1051(это теперь номер подтверждения), ldata = 80 (вечно ни болтают, когда не нужно: )) ---> A

И, наконец, ещё одна итерация, чтобы всё понять окончательно:

A ---> ACK = 1, seq = 1051, ack_seq = 3001 + 80(ldata)= 3081, ldata = 0 ----> B

Данные перелили и осталось только закончить эту оргию, делается это так:

A ----> ACK = 1, FIN = 1, seq = 1051, ack_seq = 3081, ldata = 0 ---> B

Теперь обязательно обрати внимание на поле ack_seq в следующем

пакете – оно увеличилось на 1, хотя данных не было:

B ---> ACK = 1, seq = 3081, ack_seq = 1051 + 1 = 1052, ldata = 0 ---> A

Всё я этих самых пор A не имеет глосса и сказать ничего не может, однако

B ещё имеет пару претензий по качеству проведенного разговора и хочет

поболтать и после чего заткнуться, окончательно послав флаг FIN, однако

B может не сказать ничего, но рассмотрим всё таки случай по сложнее:

B ----> seq = 3081, ack_seq =1052, ack = 1, ldata = 40 ----> A

A ----> ack = 1, seq = 1052, ack_seq = 3081 + 40 = 3121, ldata = 0(приёмпрошёлнормальноятебяуслышалдетка: )) ---> B

B ---> seq = 3121, ack_seq = 1052, ack = 1, FIN = 1, ldata = 0 (запросназакрытие) --> A

A ----> seq = 1052, ack_seq = 3121 + 1 = 3122, ACK = 1, ldata = 0(всё - закончили окончательно)

Вот такое нелёгкое оказывается дело – TCPIP: )) – это вам не сокеты юзать.

Всё осталось теперь всё это дело накодить, чем и займёмся.

Например, рассмотрим запрос на соединение хоста

A на хост B, используя компонент Net:: RawIP:

Запрос на соединение

#! /usr/bin/perl

use Net:: RawIP;

# это A, может быть и имя типа www. cdswom. net

$client = " 10. 4. 2. 32";

# это B

$server = " 10. 4. 2. 19";

#создаём новый объект

$packet = new Net:: RawIP;

#формируем в объекте нужные поля

$packet-> set({ip => {saddr => $client, daddr => $server},

          tcp => {source => 3000, dest => 1000, seq => 1234567890, psh => 1, syn => 1}});

#посылаем объект

$packet-> send;

Комментарии:

Ну что, какой ещё язык сможет собрать пакет в10 строчек? Вот и я говорю Perl рулит: ). Итак, первое

что мы делаем – это инициализируем пакет $packetстандартным методом, потом вызываем конструктор set, о нём поподробней:

Сначала вызываем конструктор для заголовка ip, где:

Saddr – ip-адрес отправителя, да, дорогой мой, можно сделатьспуфинг – это же гайка, просто скажи кого ты хочешь подставить: )

Daddr – адрес получателя, куда шлём собственно.

Всё больше нам в этом пакете ничего не надо –всё остальное за нас сделает модуль.

Следующим параметром идёт tcp и конструктор для него, но может с таким же успехом идти и udp, но мы всё жеговорим о tcp:

Source – порт от куда отправляют сообщение, туда же придётответ, так что снифер надо настраивать именно на этот порт.

Dest – удалённый порт куда идёт пакет

Seq – наш начальный номер последовательности, как я сказал любой.

Syn – устанавливаем флаг в еденицу, то есть говорим, что хотимдобиться подключения и флаг psh - говорит о том, что немедленнои не фиг ставить в очередь: ) – необязательный параметр.

Всё после этого сервер должен послать ответ, формат ты уже знаешьосталось поймать ответ это делаеться очень просто с помощью функции pcapinit:

$filter = " src port 3000";

$device = “eth0”; # идентификатор сетевой карточки в системе, если одна это и есть

$psize = 1500; # размер пакета на приём(максимальный)

$timeout = 500; # сколько ждать ответа, привета

$ans = new Net:: RawIP; #инициализируем новый объект

my $pcap = $ans-> pcapinit($device, $filter1, $psize, $timeout); #вызываемфункцию

После того как прошёл тайм аут и мы получили пакет необходимовыбить из него какие-то параметры делается это с помощьюметода get:

($tos, $urg, $ack, $psh, $rst, $syn, $fin) = $ans-> get({

ip => [qw(tos)],

tcp => [qw(psh syn urg ack rst fin)]

});

Порядок не важен, главное названия. Ну и всё – соответствующиепеременные содержат соответствующие значения и делай с ними, что хочешь: ).



  

© helpiks.su При использовании или копировании материалов прямая ссылка на сайт обязательна.