Ru-Board.club
← Вернуться в раздел «Прикладное программирование»

» FTP Client на C#

Автор: AndrewWork
Дата сообщения: 09.05.2003 01:45
Доброго времени суток.
Приведу часть программы, которая получает список дирикторий с сервера.
public string ListDir()
{
string command ="";
Socket sss = getDataSocket ();
command = "LIST";
command +="\r\n" ;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Response();

Byte[] readBytes;
int sizeReceived;
string serverMessage = "";
do
{
readBytes = new Byte[READ_BUFFER_SIZE];
sizeReceived = sss.Receive(readBytes, readBytes.Length, SocketFlags.None);
serverMessage += Encoding.ASCII.GetString(readBytes, 0, sizeReceived);
if ( sizeReceived == 0 ) break ;
}
while (true); //sizeReceived == readBytes.Length & !serverMessage.EndsWith("\r\n")); // Go back for more if necessary

return serverMessage;
if (firstTime)
{
Response();
firstTime = false;
}


}
private Socket getDataSocket ()
{
Socket pasvSocket;
string command = "PASV";//PASV:Specifies that the server data transfer process is to listen for a connection request from the client data transfer process.
command += "\r\n";
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);

//if(firstTime)
//{
// Response();
// firstTime=false;
//}
// Get server IP and port address, response sample is: .... (196,1,64,106,5,129)
string fullAddress =Response();
if (fullAddress.Substring(0,3) != "227")
{
fullAddress =Response();
}
fullAddress = fullAddress.Remove(0, fullAddress.IndexOf('(') + 1);//takes only what is
fullAddress = fullAddress.Substring(0, fullAddress.IndexOf(')'));//between paranthesis
string[] addressParts = fullAddress.Split(',');
string pasvAddress = addressParts[0] + "." + addressParts[1] + "." + addressParts[2] + "." + addressParts[3];
int pasvPort = Convert.ToInt32(addressParts[4]) * 256 + Convert.ToInt32(addressParts[5]);

// Open the Data socket
pasvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
pasvSocket.Connect(new IPEndPoint(IPAddress.Parse(pasvAddress),pasvPort));

return pasvSocket;//return the created socket
}

Возможно моя участь быть избитым ногами, но всё же осмелюсь спросить - кирилицу получить (в виде русских папок и т.п.) таким образом не получается, где грабли ?
Если нет возможности обяснить - ткните меня куда-нть шоб почитать для самообразования. За любую ценную информацию буду благодарен.
Автор: v0yager
Дата сообщения: 09.05.2003 18:23
как минимум, одна собака зарыта здесь:


Цитата:
serverMessage += Encoding.ASCII.GetString(readBytes, 0, sizeReceived);


из MSDN:


Цитата:
The ASCIIEncoding class encodes Unicode characters as single 7-bit ASCII characters. This encoding only supports character values between U+0000 and U+007F.


ASCII encoding работает только с латиницей, кириллица не проходит. Для работы с кириллицей можно получить Encoding для кодовой страницы 1251. См. описание Encoding.GetEncoding(...).

Идею решения можно взять из следующего примера:


Код:
// a b c а б в г д ж з a b c
System.Byte[] dataWithRussianChars = {0x61,0x62,0x63, 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7, 0x61,0x62,0x63};

// получить Ecoding для заданной кодовой страницы
System.Text.Encoding enc = System.Text.Encoding.GetEncoding(1251);

// посмотреть на результат
System.Console.WriteLine(enc.GetString(dataWithRussianChars));

// убедиться, что ASCII таки не пропускает кириллицу
System.Console.WriteLine(System.Text.Encoding.ASCII.GetString(dataWithRussianChars));

// остановить время
System.Console.ReadLine();
Автор: AndrewWork
Дата сообщения: 10.05.2003 02:10
Огромное спасибо, v0yager.

Если это не будет наглостью ... у меня ещё маленький вопросик.
Меня интересует установка коннекта на PORT mode.
Команда PORT 10,1,1,1,17,171 передаёт значения ip адреса - 10.1.1.1 и порта по которому будут передаваться данные - port№ = 17*256+171 = 4523. При данной установке связи порт назначает клиент, тобишь я. А вот как выбирать у себя порт правильно ? Для FTP и т.п. сессия, как я понял есть диапазон портов от 1025 до 5000. Так что, просто рандомайзом ? К сожалению пока что никаких указаний на этот счёт в стандартах я не нашёл. Кто что знает/думает/полагает на этот счёт ?
Автор: v0yager
Дата сообщения: 10.05.2003 14:45
при PORT mode connect для создания канала данных будет идти от сервера к клиенту. При этом нужно быть готовым к борьбе с трудностями (если есть firewall). Для справки см. All About FTP

Порт при этом действительно выбирает клиент, можно и рандомом. В общем случае он может быть от 1024 до 65535. Порт и адрес (в терминах .NET - EndPoint) - любая свободная комбинация. Если есть firewall - нужно дополнительно учитывать его конфигурацию.

А вот тут появляется одно "НО". Причем большое. По дефолту в WinNT (начиная с 3.51 и до Win2K всех вариантов) у локального порта исходящего соединения номер может быть до 5000. При желании можно расширить диапазон - Unable to Connect from TCP Ports Above 5000. Но лучше на это не закладываться. Для универсальности желательно использовать порты 1024 - 5000.

Добавлено
Ограничение в 5000 может помешать FTP серверу IIS при дефалтовых установках установить соединение с ftp-клиентов, даже если все остальные условия (firewall, порт свободен,...) будут выполнены.

Автор: AndrewWork
Дата сообщения: 10.05.2003 22:54
Странно, но у меня как-то не получилось вот просто так подконнектиться по порту выбранному рандомом
Я делал так :

IPAddress localAddress = ((IPEndPoint)ClientSocket.LocalEndPoint).Address;
Random rnd = new Random();
int FstPortNmbr = rnd.Next(5,15);
int SndPortNmbr = rnd.Next(0,999);
string localAddressCom = localAddress.ToString().Replace(".",",")+","+FstPortNmbr.ToString()+","+SndPortNmbr.ToString();
int portPort = FstPortNmbr*256+SndPortNmbr;
string command = "PORT "+localAddressCom;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
PasvPortSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPHostEntry iphost = Dns.GetHostByName(RemoteHost);
ip = iphost.AddressList[0];
PasvPortSocket.Connect(new IPEndPoint(ip,portPort));

RemoteHost - адрес ftp соответственно.
Выбрасывает с ошибкой "No connection could be made because the target machine actively refused it". Вроде нигде не напортачил, ан вот как. Да, и исходников с FTP клиентами работающих на PORT mode я не нашёл ... неспроста это всё, неспроста ...
Автор: v0yager
Дата сообщения: 11.05.2003 09:50
при работе в PORT mode для создания канала данных не ты соединяешься с фтп-сервером, а он с тобой (см. еще раз All About FTP

а в примере, который ты привел, смешаны воедино PORT & PASV режимы.

до отправки команды

Код: ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Автор: AndrewWork
Дата сообщения: 11.05.2003 13:39
Угу, именно PORT mode. По PASV у меня клиент уже коннектить.
Автор: v0yager
Дата сообщения: 11.05.2003 14:16
тогда обрати внимание, на то что я уже написал в предыдущем посте.

Вместо
Код: PasvPortSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPHostEntry iphost = Dns.GetHostByName(RemoteHost);
ip = iphost.AddressList[0];
PasvPortSocket.Connect(new IPEndPoint(ip,portPort));
Автор: AndrewWork
Дата сообщения: 11.05.2003 15:41
Я пытаюсь, чесслово.

IPAddress localAddress = ((IPEndPoint)ClientSocket.LocalEndPoint).Address;
Random rnd = new Random();
int FstPortNmbr = rnd.Next(5,15);
int SndPortNmbr = rnd.Next(0,999);
string localAddressCom = localAddress.ToString().Replace(".",",")+","+FstPortNmbr.ToString()+","+SndPortNmbr.ToString();
int portPort = FstPortNmbr*256+SndPortNmbr; // Выбрал свободный порт
string command = "PORT "+localAddressCom;
//----------------------------------------------
TcpListener PasvPortListener = new TcpListener(localAddress,portPort);
PasvPortListener.Start(); //запустил на нем Listener
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0); //сообщил об этом серверу командой PORT
PasvPortSocket = PasvPortListener.AcceptSocket(); //Принял соединение от сервера.

Вот последняя строчка не проходит дальше.
Честно говоря мне совестно занимать твоё время, из-за своих проблем, может есть какой-нть ftp клиент работающий на PORT mode с исходниками ?
Автор: rew
Дата сообщения: 11.05.2003 22:34

Цитата:
Вот последняя строчка не проходит дальше.

всмысле тормозит выполнение программы? так оно скорей всего ждет что кто то подключится. видимо перед этим нужно послать какую нить команду типа "LIST" например...
Автор: AndrewWork
Дата сообщения: 11.05.2003 23:43
LIST проходит по каналу control. По моему это тут непричём (да и эксперимент показал). Тут похоже дело просто в технике ... думается мне я не правильно принимаю соединение сервера.
Автор: rew
Дата сообщения: 11.05.2003 23:48
что бы ждать что то на канале данных (я хоть почти с c# не знаком но PasvPortListener.AcceptSocket() это вроде как ты ждешь соединения на выбранном порту)нужно что нибудь запросить у сервера, после команды лист сервер должен послать через канал данных список файлов, а по командному каналу возвращает ответ, удалась ли передача или нет

Добавлено
AndrewWork
кста после того как шлешь команду, нужно вроде получить ответ сервера

Добавлено
кста забывать про
Цитата:
Учти также, что выбранный рандомом порт уже может быть занят другим клиентским приложением: если получил исключение - исчешь дальше.
тоже не стоит
Автор: AndrewWork
Дата сообщения: 12.05.2003 00:03
Ok, я делаю так :
IPAddress localAddress = ((IPEndPoint)ClientSocket.LocalEndPoint).Address;
Random rnd = new Random();
int FstPortNmbr = rnd.Next(5,15);
int SndPortNmbr = rnd.Next(0,999);
string localAddressCom = localAddress.ToString().Replace(".",",")+","+FstPortNmbr.ToString()+","+SndPortNmbr.ToString();
int portPort = FstPortNmbr*256+SndPortNmbr;
string command = "PORT "+localAddressCom;
command = "LIST \r\n";
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Response(); // Получаю ответ "150 Opening ASCII mode data connection for /bin/ls."
TcpListener PasvPortListener = new TcpListener(localAddress,portPort);
PasvPortListener.Start();
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
PasvPortSocket = PasvPortListener.AcceptSocket();
На последней строчке всё так же зависает ... ждёт коннекта с сервера ...
Автор: rew
Дата сообщения: 12.05.2003 00:06

ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
command = "LIST \r\n";
Response(); // Получаю ответ "150 Opening ASCII mode data connection for /bin/ls."
TcpListener PasvPortListener = new TcpListener(localAddress,portPort);
PasvPortListener.Start();
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
PasvPortSocket = PasvPortListener.AcceptSocket();


Добавлено
всмысле ты 2 раза слал LIST
Автор: AndrewWork
Дата сообщения: 12.05.2003 00:27
Нет, я ошибся в копировании строк сюда. Сначала я отсылаю
string command = "PORT "+localAddressCom;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
потом команду LIST
command = "LIST \r\n";
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Затем слухаю сервер ... и похоже не слышу
Автор: rew
Дата сообщения: 12.05.2003 00:35
может int portPort = FstPortNmbr*256+SndPortNmbr; нужно наоборот? я не помню точно но попробуй int portPort = FstPortNmbr+SndPortNmbr*256;
кроме того, может это не кртично (хз, но хуже не будет), но всегда получай ответ:
string command = "PORT "+localAddressCom;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Response();
...
command = "LIST \r\n";
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Response();


Добавлено
после "string command = "PORT "+localAddressCom;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0); "
получил ответ "200 PORT command OK"?
Автор: AndrewWork
Дата сообщения: 12.05.2003 00:54
Нужно именно portPort = FstPortNmbr*256+SndPortNmbr. Так написано и в MSDN и в rfc.
Команды "200 PORT command OK" в ответ не получаю. Странно это кстати ... вообще после
string command = "PORT "+localAddressCom;
ClientSocket.Send(Encoding.ASCII.GetBytes(command), command.Length, 0);
Response(); ничего не получает ...
Автор: rew
Дата сообщения: 12.05.2003 01:13
AndrewWork
наверно ты послал серверу команду установить пассивный(или вот не помню активный?) режим, это всмысле когда он ждет что ты подключишся к нему, а не он к тебе. я когда разбирался с фтп клиентом (на жаве правда) последовательность команд была следущая
1 "USER "+username
принять ответ типа "331 Password required"
2 "PASS "+pass
принять ответ (тут как правило "заставка" на много строк) типа "230 Logged in, proceed"
3 "TYPE I"
принять ответ типа "200 Type: I"
4 "pwd"
принять ответ типа "257 "/""
5 "TYPE A"
принять ответ типа "200 Type: A N"
6 "PORT "+...
принять ответ типа "200 PORT command OK"
7 "LIST"
принять ответ типа "150 Opening data connection"
слушать порт
получить данные через полученый сокет
закрыть сокет данных
принять ответ типа "226 Transfer completed"
Автор: v0yager
Дата сообщения: 12.05.2003 08:31
AndrewWork

Цитата:

int SndPortNmbr = rnd.Next(0,999);


в команде PORT h1,h2,h3,h4,p1,p2

h1-h4, p1-p2 это байты! (0-255) О чем думает сервер, получив в младшем байте номера порта значение до 999 можно только догадываться

На твоей машине работают другие клиенты в PORT mode? Если да, то посмотри лог их работы (порядок команд и ответов) и сравни с тем, что делаешь ты.

Дополнительно, было бы хорошо поставить один из сетевых мониторов и записать весь траффик при запуске твоего клиента. И, опять же, стравнить с поведением других фтп-клиентов.
Автор: rew
Дата сообщения: 12.05.2003 09:12
да просто в консоли netstat`ом можно видеть кто по какому порту подключен
Автор: AndrewWork
Дата сообщения: 12.05.2003 20:20
v0yager, за замечание о p1, p2 - огромный thank's.
А насчёт других клиентов - так я их часто использую для сравнения ... у меня всё один в один идёт с ними, и обращение моё м сервером я записывал, в часности Ириской.

rew - по моему так TCPView от Sysinternals лучше, сам обновляется, висит не мешает
Автор: rew
Дата сообщения: 12.05.2003 21:53

Цитата:
по моему так TCPView от Sysinternals лучше

с точки зрения удобства юзания, понятно что хуже нетстата ничаво не придумаешь тока вот он всегда есть и его не нужно инсталить
зы так получилось?
Автор: AndrewWork
Дата сообщения: 14.05.2003 00:14
Чёрт, походу я совсем запутался ... никак не связаться с сервером по PORT. Кто-нибудь имевший опыт в этом деле, отзовитесь.
Я нашёл в каких - то исходниках такой пример:
private Socket CreateDataSocketActive()
{
// create listening socket at a system allocated port
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// choose any port
IPHostEntry localHostEntry = Dns.Resolve(Dns.GetHostName());
IPEndPoint localEndPoint = new IPEndPoint(localHostEntry.AddressList[0], 0);
sock.Bind(localEndPoint);
// queue up to 5 connections
sock.Listen(5);
// get the listen port
int port = ((IPEndPoint)sock.LocalEndPoint).Port;
IPAddress addr = ((IPEndPoint)sock.LocalEndPoint).Address;
// find out ip & port we are listening on
SetDataPort((IPEndPoint)sock.LocalEndPoint);
return sock;
}
private void SetDataPort(IPEndPoint ep)
{
byte[] hostBytes = ep.Address.GetAddressBytes();
byte[] portBytes = ToByteArray((ushort)ep.Port);
// assemble the PORT command
string cmd = new StringBuilder("PORT ").
Append((short)hostBytes[0]).Append(",").
Append((short)hostBytes[1]).Append(",").
Append((short)hostBytes[2]).Append(",").
Append((short)hostBytes[3]).Append(",").
Append((short)portBytes[0]).Append(",").
Append((short)portBytes[1]).ToString();
// send command and check reply
string reply = SendCommand(cmd);
ValidateReply(reply, "200");
}
Так даж повторить не удаётся. Сокет не устонавливает соединение нивкакую ... уже начинаю думать что тут ошибка в ДНК
Автор: rew
Дата сообщения: 14.05.2003 00:20
может сервер не поддерживает этот режим? те может я гоню, но на сколько я помню есть фтп сервера, которые не поддерживают один из 2 режимов, какой правда не помню...
Автор: v0yager
Дата сообщения: 14.05.2003 08:46
2AndrewWork

запиши в пост ссылку, с которой ты снял пример фтп-клиента на с# (исходная версия) Если есть возможность выложить архив с измененной тобой версией (текущей) - тоже.

Отлаживать программу через форум публикуя то одну функцию, то другую можно еще долго.

Дополнительно, напиши адрес фтп-сервера, на котором ты тестируешь клиент (если это не внутренний сервер, конечно).

Кстати, между тобой и сервером есть прокси, файрволлы?



Автор: AndrewWork
Дата сообщения: 14.05.2003 09:51
Пример работы с PORT mode взят мною отсюда - http://www.enterprisedt.com/downloads/csftp/ftp-0.9.zip
FTP сервер на котором тестирую клиент - внутренний. FTP серверов тут много, и многие поддерживают PASV mode, но некоторые админы для безопасности открывают только PORT mode (это связано и с использованием файрволов в том числе). С моей стороны на время работы все фаирволы и прокси я отключаю, так что в этом проблемы быть не может. Выложить мои исходники вряд ли возможно, так как трафик платный, но могу скинуть на мыло.
Автор: v0yager
Дата сообщения: 14.05.2003 10:37
2AndrewWork

мой почтовый адрес у тебя в ПМ
Автор: AndrewWork
Дата сообщения: 14.05.2003 19:25
отправил
Автор: v0yager
Дата сообщения: 15.05.2003 14:40
2AndrewWork

сообщение по почте с сорсами получил.

Сначала о примере фтп-клиента с адреса:http://www.enterprisedt.com/downloads/csftp/ftp-0.9.zip

это библиотека для работы с фтп с исходным кодом, лицензией GNU. Она собирается и работает, добротно сделана. Мне пришлось подправить ее для .NET v1.0 (написать свой GetAddressBytes), в остальном - все о.к. Я проверил ее на внутреннем фтп-сервере и в PORT mode, и в PASV mode - работает (логин, списки файлов, прием-передача).

После разборок с библиотекой я внимательно ознакомился с присланными сорсами. Основной вывод после проведения анализа: полный переход на эту библиотеку для работы с фтп является целесообразным и съекономит тебе много времени.

Существующий код отлаживать и доводить до рабочего состояния не имеет смысла. Там много фрагментов (взятых из разных источников), работающих в отдельности, но слабо согласованных между собой. Для того, что бы они заработали вместе, тебе придется сделать аналогичную библиотеку и все равно переделать свою программу полностью. Заставить стабильно работать то, что есть, тебе, с большой степенью вероятности, не удасться (или займет много времени). Так что лучше сразу взять фтп-библиотеку и сосредоточиться на интерфейсе и прикладных алгоритмах.

Заметка на будущее: код GUI лучше не смешивать с реализациями алгоритмов, к UI никакого отношения не имеющего. Например, фтп-клиент. Иначе отлаживать такое хозяйство будет трудно, да и с повторным использованием разработок будут проблемы. Пример из присланных сорсов: с повторным использованием FTPBuild, наследованного от System.Windows.Forms.Form в большинстве случаев будут трудности - например, в ASP.NET приложении.
Автор: AndrewWork
Дата сообщения: 15.05.2003 15:57
Спасибо, v0yager за помощь. Твоя помощь неоценима. Пожалуй я поступлю как ты советуешь. Если это не затруднит - выложи либо сюда либо мне на мыло вид исправленной функции GetAddressBytes.

Страницы: 12

Предыдущая тема: нада программку наваять...


Форум Ru-Board.club — поднят 15-09-2016 числа. Цель - сохранить наследие старого Ru-Board, истории становления российского интернета. Сделано для людей.