Модераторы: gambit
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [C#] FTP клиент. Переделать с пассивного в актиный 
:(
    Опции темы
alexuni
  Дата 4.12.2009, 23:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 16.10.2009

Репутация: нет
Всего: нет



Название темы: "[C#] FTP клиент. Переделать с пассивного в актиный режим обмена"
Всем добрый вечер! Есть FTP клиент, реализующий команды USER, PASS, RETR, STOR, RESt, Quit, LIST и PWD
Режим обмена с сервером - пассивный. Необходимо переделать в активный режим. Нужна ваша помощь! Ниже привожу код
FTP.cs
Код


using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;

namespace ftp
{
    public class FTP
    {
        public string server;
        public string user;
        public string pass;
        public int port;
        public int timeout;

        private string responseStr;
        private long bytes_total;
        private Socket main_sock;
        private IPEndPoint main_ipEndPoint;
        private Socket data_sock;
        private IPEndPoint data_ipEndPoint;
        private FileStream file;
        private int response;
        private string bucket;
        
        public FTP(string server, int port, string user, string pass)
        {
            this.server = server;
            this.user = user;
            this.pass = pass;
            this.port = port;
            main_sock = null;
            main_ipEndPoint = null;
            data_sock = null;
            data_ipEndPoint = null;
            file = null;
            bucket = "";
            bytes_total = 0;
            timeout = 10000;    // 10 секунд
        }

        public bool IsConnected
        {
            get
            {
                return main_sock != null && main_sock.Connected;
            }
        }
        
        public string ResponseString
        {
            get
            {
                return responseStr;
            }
        }
        public long BytesTotal
        {
            get
            {
                return bytes_total;
            }
        }
        private void Fail()
        {
            Disconnect();
            throw new Exception(responseStr);
        }
        // отправляет команду в сокет
        private void SendCommand(string command)
        {
            Byte[] cmd = Encoding.ASCII.GetBytes((command + "\r\n").ToCharArray());
            main_sock.Send(cmd, cmd.Length, 0);
        }
        // дожидается приема данных. Если даные пришли, то bucket +=, иначе выход по тайм-ауту
        private void FillBucket()
        {
            Byte[] bytes = new Byte[512];
            int msecs_passed = 0;
            // крутимся здесь, пока не получим ответ или тайм-аут
            while(main_sock.Available < 1)
            {
                System.Threading.Thread.Sleep(50);
                msecs_passed += 50;
                if (msecs_passed > timeout)
                {
                    Disconnect();
                    throw new Exception("Timed out waiting on server to respond.");    
                }
            }
            // пытаемся прочитать данные
            while(main_sock.Available > 0)
            {
                long bytesgot = main_sock.Receive(bytes, 512, 0);
                bucket += Encoding.ASCII.GetString(bytes, 0, (int)bytesgot);
                System.Threading.Thread.Sleep(50);
            }
        }
        // из буфера bucket извлекаем строку (до '\n')
        private string GetLineFromBucket()
        {
            int i;
            if ((i = bucket.IndexOf('\n')) < 0)
            {
                while(i < 0)
                {
                    FillBucket();
                    i = bucket.IndexOf('\n');
                }
            }
            string buf = bucket.Substring(0, i);
            bucket = bucket.Substring(i + 1);
            return buf;
        }
        // читаем ответ от сервера
        // responseStr содержит прочитанную функцию

        private void ReadResponse()
        {
            string buf;

            while(true)
            {
                // читаем строку из вх.буфера
                buf = GetLineFromBucket();
                if (Regex.Match(buf, "^[0-9]+ ").Success)
                {
                    responseStr = buf;
                    response = int.Parse(buf.Substring(0, 3));
                    break;
                }
            }
        }
       [color=red] 
     // открывает сокет в пассивном режиме
        private void OpenDataSocket()
        {
            string[] pasv;

            Connect();
            SendCommand("PASV");
            ReadResponse();
            if (response != 227)
                Fail();
            // извлекаем инфу об IP:port от сервера
            try
            {
                int i1 = responseStr.IndexOf('(') + 1;
                int i2 = responseStr.IndexOf(')') - i1;
                pasv = responseStr.Substring(i1, i2).Split(',');
            }
            catch (Exception)
            {
                Disconnect();
                throw new Exception("Malformed PASV response: " + responseStr);
            }
            // д.б. 4 эл-та IP + 2 эл-та порта
            if (pasv.Length < 6)
            {
                Disconnect();
                throw new Exception("Malformed PASV response: " + responseStr);
            }
            // восстанавливаем IP-адрес сервера и порт
            string server_ = String.Format("{0}.{1}.{2}.{3}", pasv[0], pasv[1], pasv[2], pasv[3]);
            int port_ = (int.Parse(pasv[4]) << 8) + int.Parse(pasv[5]);
            try
            {
                // закрываем текущее соединеие для данных
                CloseDataSocket();
                // создаем подключение к новому порту
                data_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                data_sock.ReceiveTimeout = 10000;
                data_sock.SendTimeout = 10000;
                data_ipEndPoint = new IPEndPoint(Dns.GetHostEntry(server_).AddressList[0], port_);
                data_sock.Connect(data_ipEndPoint);
            }
            catch (Exception ex)
            {
                throw new Exception("Failed to connect for data transfer: " + ex.Message);
            }
        }[/color]

        // закрываем соединение для данных
        private void CloseDataSocket()
        {
            if (data_sock != null)
            {
                if (data_sock.Connected)
                    data_sock.Close();
                data_sock = null;
            }
            data_ipEndPoint = null;
        }
        // закрываем все соединения, открытые для работы с сокетом
        public void Disconnect()
        {
            CloseDataSocket();
            if (main_sock != null)
            {
                if (main_sock.Connected)
                {
                    SendCommand("QUIT");
                    ReadResponse();
                    main_sock.Close();
                }
                main_sock = null;
            }
            if (file != null)
                file.Close();
            main_ipEndPoint = null;
            file = null;
        }
        // подключение с указанием сервера, порта, имени пользователя и пароля
        public void Connect(string server_, int port_, string user_, string pass_)
        {
            server = server_;
            user = user_;
            pass = pass_;
            port = port_;

            Connect();
        }
        // создает управляющее подключение
        public void Connect()
        {
            //проверяем все параметры
            if (server == null)
                throw new Exception("No server has been set.");
            if (user == null)
                throw new Exception("No username has been set.");

            if (main_sock != null)
                if (main_sock.Connected)
                    return;

            //создаем подключение
            main_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            main_sock.ReceiveTimeout = 10000;
            main_sock.SendTimeout = 10000;
            main_ipEndPoint = new IPEndPoint(Dns.GetHostEntry(server).AddressList[0], port);
            
            // пытаемся подключиться
            try
            {
                main_sock.Connect(main_ipEndPoint);    
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }

            // читаем ответ
            ReadResponse();
            if (response != 220)
                Fail();

            // отправляем имя пользователя и пароль
            SendCommand("USER " + user);
            ReadResponse();

            switch(response)
            {
                 
                case 331:
                    if (pass == null)
                    {
                        Disconnect();
                        throw new Exception("No password has been set.");
                    }
                    SendCommand("PASS " + pass);
                    ReadResponse();
                    if (response != 230)
                        Fail();
                    break;
                    // мы подключились
                case 230:
                    break;
            }

            SetASCIIMode();
            return;
        }
        // получает список файлов на удаленном сервере
        public ArrayList List()
        {
            Byte[] bytes = new Byte[512];
            string file_list = String.Empty;
            int msecs_passed = 0;
            ArrayList list = new ArrayList();
        
            Connect();  // !!!!!!!!!!!!!!!!!!!
            OpenDataSocket();
            SendCommand("LIST");
            ReadResponse();

            switch(response)
            {
                    // все хорошо
                case 125:
                case 150:
                    break;
                    // плохо
                default:
                    CloseDataSocket();
                    throw new Exception(responseStr);
            }

            // если ответов еще нету, то ожидаем
            while(data_sock.Available < 1)
            {
                System.Threading.Thread.Sleep(50);
                msecs_passed += 50;
                if (msecs_passed > (timeout / 10))
                    break;
            }

            // данные все-таки есть?
            while(data_sock.Available > 0)
            {
                long bytesgot = data_sock.Receive(bytes, bytes.Length, 0);
                file_list += Encoding.ASCII.GetString(bytes, 0, (int)bytesgot);
                //!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                System.Threading.Thread.Sleep(50);
            }
            
            CloseDataSocket();
            
            // получаем итоговый ответ от сервера
            ReadResponse();
            if (response != 226)
                throw new Exception(responseStr);

            // преобразуем string -> ArrayList
            foreach(string f in file_list.Split('\n'))
            {
                if (f.Length > 0 && !Regex.Match(f, "^total").Success)
                    list.Add(f.Substring(0, f.Length - 1));
            }

            return list;
        }
        // смена директории
        public void ChangeDir(string path)
        {
            Connect();
            SendCommand("CWD " + path);
            ReadResponse();
            if (response != 250)
                throw new Exception(responseStr);
        }

        // получить размер файла
        public long GetFileSize(string filename)
        {
            Connect();
            SendCommand("SIZE " + filename);
            ReadResponse();
            if (response != 213)
                throw new Exception(responseStr);
            return Int64.Parse(responseStr.Substring(4));
        }
        // испр. на ASCII
        private void SetASCIIMode()
        {
            SendCommand("TYPE A");
            ReadResponse();
            if (response != 200)
                Fail();
        }
        // загрузить файл на сервер с докачкой
        public void OpenUpload(string filename, string remote_filename, bool resume)
        {
            OpenDataSocket();
            bytes_total = 0;
            try
            {
                file = new FileStream(filename, FileMode.Open);
            }
            catch(Exception ex)
            {
                file = null;
                throw new Exception(ex.Message);
            }
            // если восст. докачку, то посылаем команду восст. докачки
            if (resume)
            {
                // для этого определяем, докуда докачали
                long size = GetFileSize(remote_filename);
                SendCommand("REST " + size);
                ReadResponse();
                if (response == 350)
                    file.Seek(size, SeekOrigin.Begin);
            }
            // сохраняем файл на сервер
            SendCommand("STOR " + remote_filename);
            ReadResponse();
            switch(response)
            {
                    // все нормально
                case 125:
                case 150:
                    break;
                default:
                    file.Close();
                    file = null;
                    throw new Exception(responseStr);
            }
            return;
        }
  
        // загружает файл на сервер, пока не будет скачан
        public long DoUpload(bool isAbort)
        {
            Byte[] bytes = new Byte[512];
            long bytes_got;

            try
            {
                bytes_got = file.Read(bytes, 0, bytes.Length);
                bytes_total += bytes_got;
                data_sock.Send(bytes, (int)bytes_got, 0);

                if(bytes_got <= 0 || isAbort)
                {
                    // файл докачан
                    file.Close();
                    file = null;
                
                    CloseDataSocket();
                    ReadResponse();
                    switch(response)
                    {
                        case 226:
                        case 250:
                            break;
                        default:
                            throw new Exception(responseStr);
                    }
                }
            }
            catch(Exception)
            {
                file.Close();
                file = null;
                CloseDataSocket();
                ReadResponse();
                throw;
            }

            return isAbort ? 0 : bytes_got;
        }
    
    }
}

PM MAIL   Вверх
tol05
Дата 5.12.2009, 01:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 6
Всего: 170



Цитата

In order to use active mode, the client sends a PORT command, with the IP and port as argument. 
ссылка отсюда

и сразу вопрос. Класс System.Net.FtpWebRequest чем не подходит?

ИМХО гораздо более человечный к разработчику

Это сообщение отредактировал(а) tol05 - 5.12.2009, 01:14


--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
alexuni
Дата 6.12.2009, 13:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 16.10.2009

Репутация: нет
Всего: нет



tol05, благодарю за совет! Буду пробовать делать... 
PM MAIL   Вверх
alexuni
Дата 7.12.2009, 14:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 16.10.2009

Репутация: нет
Всего: нет



Изменил фрагмен с пассивным режимом на активный
Код

 SendCommand("PORT 127,0,0,1,14,16");
            ReadResponse();
            if (response != 200)
                Fail();


Сервак пишет, что порт команда выполнилась успешно. Но, на стороне клиента выскакивает ошибка, что принимающая сторона заболокировала data_connection в этом фрагменте. (просматривал по шагам : data_ipEndPoint - корректный вида ip:port) Притом, что файервол отключил. Пожалуйста, помогите решить проблему!
     
Код

       try
            {
                // закрываем текущее соединеие для данных
                CloseDataSocket();
                // создаем подключение к новому порту
                data_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                data_sock.ReceiveTimeout = 10000;
                data_sock.SendTimeout = 10000;
                data_ipEndPoint = new IPEndPoint(Dns.GetHostEntry("127.0.0.1").AddressList[0], 3600);
                data_sock.Connect(data_ipEndPoint);
            }
            catch (Exception ex)
            {
                throw new Exception("Failed to connect for data transfer: " + ex.Message);
            }


Это сообщение отредактировал(а) alexuni - 7.12.2009, 14:07
PM MAIL   Вверх
tol05
Дата 7.12.2009, 17:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 6
Всего: 170



какой exception точно выскакивает? Вы можете привести его тип и его текст дословно?

Если это SocketException, то имеет смысл
Цитата

При получении исключения SocketException воспользуйтесь свойством SocketException..::.ErrorCode, чтобы получить определенный код ошибки. Получив этот код, можно обратиться за подробным описанием ошибки к документации по кодам ошибок API Windows Socket версии 2 в библиотеке MSDN.


Ошибка еще может быть в методе CloseDataSocket()
Код

if (data_sock.Connected)
                    data_sock.Close(); //Close() может быть не вызван если Connected == false. 
                                                //Но ресурсы сокета все равно освобождать нужно? А след. срока сделает это невозможным
                data_sock = null;



--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
alexuni
Дата 8.12.2009, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 16.10.2009

Репутация: нет
Всего: нет



Точная ошибка : "Failed to connect for data transfer. Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение "

Цитата

Ошибка еще может быть в методе CloseDataSocket()


Но ведь в пассивном режиме все работает слаженно!


PM MAIL   Вверх
tol05
Дата 8.12.2009, 14:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1632
Регистрация: 21.12.2006
Где: Харьков

Репутация: 6
Всего: 170



Цитата(alexuni @  8.12.2009,  12:13 Найти цитируемый пост)
"Failed to connect for data transfer.


погодите-ка

Но ведь в активном режиме серве должен подключаться к клиенту, а не клиент к серверу. Разве нет?
Цитата

In active mode, the FTP client opens a dynamic port, sends the FTP server the dynamic port number on which it is listening over the control stream and waits for a connection from the FTP server

Где тогда у Вас методы Listen, Accept?

Клиент отправляет серверу свой ендпоинт и входит в прослушивание. Ждет когда сервер к нему подключится.

А в пассивном режиме все как раз наоборот. Сервер слушает, а клиент коннектится.



--------------------
На хорошей работе и сны хорошие снятся.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
cully
mr.DUDA
Exception

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, cully, mr.DUDA, Exception.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Распределённые приложения и сеть | Следующая тема »


 




[ Время генерации скрипта: 0.0766 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.