Консультация № 175035
08.12.2009, 20:14
35.00 руб.
0 11 1
Здравствуйте, уважаемые эксперты!
У меня возникла учебная задача написать FTP сервер - простенький консольный с использованием только winsock2 и соответственно океты для коммуникации. Пишу в Visual Studio 2008 - хотя это и неважно. С++.
Все вроде описано в RFC, но непонятно все же как происходит обмен данными в ASCII режиме или в бинарном.
Сначала хочется разобраться с ASCII режимом так как по команде от клиента "LIST" сервер должен вернуть список всех файлов и папок - а он передается в ASCII режиме. Но в RFC никак немогу понять как это происходит.
Парсинг команд я сделал - а вот как реагировать на команду "LIST" неочень понятно.
Собственно хотелось бы получить пример кода - что происходит после приема команды "LIST" от клиента. Как происходит соединение для передачи днных с клиентом (пассивный или активный режим... вроде мне говорили что просто connect к соответствующему порту по адресу клиента. команду "PORT" я уже парсил и записывал адрес и порт клиента) и как передается список файлов.
сессия горит)
Заранее спасибо!

Обсуждение

Неизвестный
09.12.2009, 09:45
общий
это ответ
Здравствуйте, Пишко Александр.
Если вы распарсили команду port, то у вас должно быть 2 числа: ip и tcp порт.
После приема команды list вам нужно открыть соединение на соответствующий ip и порт (через другой сокет) и отправить через него список файлов в таком примерно виде
-rw-r--r-- 1 user user 20856 Aug 27 10:23 file_name\r\n
при этом через управляющее соединение нужно послать
150 Opening ASCII mode data connection for file list
когда подключитесь и
226 Transfer complete
когда весь список файлов будет отправлен
если получена команда pasv, то нужно ответить
227 Entering Passive Mode (127,0,0,1,135,18).
в скобках указать ip/порт сокета, который получилось открыть для прослушивания (отдельный сокет! не тот, который слушает соединения для передачи команд)
после команды list ждать подключения к этому сокету, после подключения, послать список, как указано выше и закрыть слушающий сокет до следующей команды pasv.
еще желательно понимать команды eprt и epsv (то же самое для ipv6)
5
все немножко прояснилось)
Неизвестный
09.12.2009, 15:02
общий
vladisslav:
Спасибо Владислав. Все немножко прояснилось.
Но как я понял (для передачи данных) это клиент должен делать listen на соответствующем порту (20 если не пассивный режим, другой если была команда порт) а сервер уже делает connect к клиенту с соответствующим сокетом (если не ошибаюсь =) ).
И все таки неочень понятно - когда клиент должен понять что передача списка закочилась. в RFC написано что надо вроде "\n\n" послать.
Неизвестный
09.12.2009, 15:30
общий
Пишко Александр:
Клиент делает listen на том ip и порту, на каких сможет и посылает команду port с такими ip и портом, какие будут видны со стороны сервера (в случае наличия файрволов между сервером и клиентом, они могут отличаться от тех, которые будут слушаться). В клиенте может быть предусмотрена возможность указать, какие порты слушать и что посылать в команде port, для прохождения файрволов.
Клиент поймет, что передача закончилась, когда получит
226 Transfer complete
К тому же сервер после окончания передачи списка закрывает соединение.
Если проверить с помощью telnet и netcat, то получается что последние 2 принятых байта: 0x00,0x0a, (для ProFTPD Version 1.3.1), то есть клиент может ждать нулевого байта.
Да и вообще это забота клиента . Сервер должен после отправки последнего буфера, как только сокет станет вновь доступен для записи, послать
226 Transfer complete
по командному каналу и закрыть соединение по каналу данных.
Насчет 20 порта... мне не удалось поймать соединение на 20 порт, правда проверял на той же машине, где стоит сервер. В общем на практике клиент всегда посылает команду port, так как чтобы слушать 20 порт нужны права рута, которых как правило нет.
Неизвестный
09.12.2009, 15:58
общий
А все таки что же должен слушать и делать connect? я сейчас сделал connect на сервере к сокету, созданному по данным от команды PORT - соединение установилось и послал туда просто имя файла и закрыл сокет и после этого послал "226 transfer complited" и все работает.
if(connect(ses->sock_data,(sockaddr*)&(ses->addr_data),sizeof(ses->addr_data))==SOCKET_ERROR)
{
int kk=WSAGetLastError();
cout<<"\nLast error code: "<<kk<<"\n";
}else
{
cout<<"Data connection OK\n";
send_client_message(ses,FTP_DATACONN,"data connection established");
send_data2client_ascii(ses);
closesocket(ses->sock_data);
send_client_message(ses,FTP_TRANSFEROK,"transfer complited");
}
Это же вроде и есть пассивный режим. Клиент отправлял команду порт. Но тогда как я создам сокет для connect если я незнаю адрес клиента? Порт понятно будет 20.

PS послал Вам благодарность. дошла ли?)
Неизвестный
09.12.2009, 16:11
общий
Пишко Александр:
Формат команды PORT:
PORT ip,ip,ip,ip,port,port
то есть если клиент послал
PORT 127,0,0,1,8,5
то получаем
ip 127.0.0.1
port 8*256+5=2053
то есть 127.0.0.1:2053
на этот адрес надо делать connect для создания соединения данных
Неизвестный
09.12.2009, 16:18
общий
Ну это понятно. Это я меня и было сделано. Данные передаются. Просто у меня небольшая каша образовалась с простым и пассивным режимом.
Препод говорил что вся разница в том, на какой клиентский порт делать connect - 20 стандартный (простой режим) и тот, который определен в команде PORT (пассивный режим).
Неизвестный
09.12.2009, 16:26
общий
Пишко Александр:
port - это как раз таки простой, а не пассивный режим.
В случае пассивного режима connect не делается, а делается listen на ip/порт из настроек сервера и отправляется ответ с указанием ip/порта, как они будут видны со стороны клиента (тоже например из настроек или в, простейшем случае (белый ip), те, которые слушаем) в формате команды порт. А уже клиент делает connect на эти ip/порт и ждет от нас данных.
То есть нужно поймать соединение, отправить листинг или файл, закрыть полученный сокет, закрыть слушающий сокет до следующей команды pasv.
Неизвестный
09.12.2009, 16:36
общий
vladisslav:
Тоесть клиент посылает PASV затем PORT с настройками а сервер должен слушать этот ip и порт? Ну потом естественно уже все происходит как и в обычном режиме.
Неизвестный
09.12.2009, 16:44
общий
Пишко Александр:
Нет. Прочитайте внимательно мой ответ.
Клиент посылает только pasv.
Сервер создает слушающий сокет (ip и порт берет например из настроек или через UPNP или протестировав через какой-нибудь сервис) и отвечает клиенту
227 Entering Passive Mode (127,0,0,1,135,18).
указав в скобках, как будет виден открытый сокет со стороны клиента (из настроек берет или еще как-то, если учитывать файрволы)
Клиент читает то, что в скобках и пытается присоединиться по соответствующему ip/порту (в данном случае 127.0.0.1:34578)
Сервер ловит соединение и посылает/принимает то, что от него хочет клиент.
Неизвестный
09.12.2009, 16:49
общий
Теперь понятно. Еще раз спасибо.
Просто я думал что всё то, что посылается как reply от сервера клиенту "код+сообщение" - клиент парсит только код - а то что дальще это так - мишура.
Неизвестный
10.12.2009, 01:49
общий
Пишко Александр:
Чтобы уточнить поведение правильного сервера, желательно поставить себе какой-нибудь бесплатный рабочий ftp и подключаться к нему с заданной последовательностью команд. Даже не через телнет, а своей консольной программой. Тогда быстрее находятся дырки в своем протоколе. Иногда из описания непонятно, что какое-то действие обязательное или опциональное.
Я тоже этим заинтересовался недавно, но экзамены не дают делать интересное :(
Я на xlight тренировался.
Форма ответа