Xây dựng FTP client với kỹ thuật lập trình socket

    0

    Trong bài học này chúng ta sẽ cùng xây dựng một phần mềm FTP client đơn giản với các tính năng cơ bản. Đây sẽ là một ứng dụng với giao diện dòng lệnh. Chúng ta không xây dựng giao diện đồ họa để giữ code đủ đơn giản.

    Giới thiệu

    Ứng dụng này sẽ có các chức năng sau: Kết nối tới server, đăng nhập, đăng xuất, lấy danh sách file và thư mục trong thư mục hiện hành, tạo thư mục mới, xóa thư mục, chuyển thư mục hiện hành, hiện tên thư mục hiện hành, tải file lên server, tải file từ server.

    Cuối cùng chúng ta sẽ có một ứng dụng như sau:

    Sau đây là các bước chuẩn bị:

    (1) Tạo dự án mới dạng Console App và đặt tên là FtpClient. Bạn có thể lựa chọn mẫu dự án Console App (.NET Framework) hoặc Console App dành cho .NET 5 trở lên. Hai lựa chọn này không khác biệt với bài toán này.

    (2) Trong dự án tạo class FtpClient trong file FtpClient.cs. Bạn sẽ thu được cấu trúc thư mục như sau (.NET Framework):

    Xây dựng tính năng kết nối, đăng nhập, đăng xuất

    Mở file FtpClient.cs và viết code như sau:

    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    namespace FtpProtocol {
        public class FtpClient {
            #region Properties
            TcpClient _controlSocket;
            StreamWriter _writer;
            StreamReader _reader;
            readonly BindingList<string> _responses = new BindingList<string>();
            readonly BindingList<string> _commands = new BindingList<string>();
            public string Command {
                get => _commands.Last();
                private set {
                    if (!string.IsNullOrEmpty(value)) {
                        _commands.Add(value);
                    }
                }
            }
            public string Response {
                get => _responses.Last();
                private set {
                    if (!string.IsNullOrEmpty(value)) {
                        _responses.Add(value);
                    }
                }
            }
            public IPAddress Address { get; set; }
            public string User { get; set; }
            public string Password { get; set; }
            public bool Connected => _controlSocket.Connected;
            public bool LoggedOn { get; private set; }
            #endregion
            #region Commands  
            public void Connect() {
                _responses.Clear();
                _commands.Clear();
                _controlSocket = new TcpClient();
                if (!_controlSocket.Connected) {
                    _controlSocket.Connect(Address, 21);
                    if (_controlSocket.Connected) {
                        _responses.ListChanged -= Responses_ListChanged;
                        _responses.ListChanged += Responses_ListChanged;
                        _commands.ListChanged -= Commands_ListChanged;
                        _commands.ListChanged += Commands_ListChanged;
                        _reader = new StreamReader(_controlSocket.GetStream());
                        _writer = new StreamWriter(_controlSocket.GetStream()) { AutoFlush = true };
                        StringBuilder sb = new StringBuilder();
                        Response = _reader.ReadLine();
                        if (Response.StartsWith("220-")) {
                            while (true) {
                                Response = _reader.ReadLine();
                                if (Response.StartsWith("220 ")) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            public void Login() {
                Command = string.Format("USER {0}", User);
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
                if (Response.StartsWith("331 ")) {
                    Command = string.Format("PASS {0}", Password);
                    _writer.WriteLine(Command);
                    Response = _reader.ReadLine();
                    LoggedOn = true;
                }
            }
            public void Logout() {
                if (_controlSocket.Connected && LoggedOn) {
                    Command = "QUIT";
                    _writer.WriteLine(Command);
                    Response = _reader.ReadLine();
                    if (Response.StartsWith("221 ")) {
                        LoggedOn = false;
                        _controlSocket.Close();
                    }
                }
            }
            
            #endregion        
            #region Logging
            public Action<string> ResponseListChangedHandler { get; set; }
            public Action<string> CommandListChangedHandler { get; set; }
            private void Responses_ListChanged(object sender, ListChangedEventArgs e) {
                if (e.ListChangedType == ListChangedType.ItemAdded) {
                    string response = _responses[e.NewIndex];
                    ResponseListChangedHandler?.Invoke(response);
                }
            }
            private void Commands_ListChanged(object sender, ListChangedEventArgs e) {
                if (e.ListChangedType == ListChangedType.ItemAdded) {
                    string command = _commands[e.NewIndex];
                    CommandListChangedHandler?.Invoke(command);
                }
            }
            #endregion
        }
    }

    Đây là một class khá phức tạp. Bạn cần nghiên cứu kỹ code để có thể hiểu được nội dung.

    File code này thực hiện một số nhiệm vụ sau:

    1. Tạo liên kết TCP tới server ở địa chỉ IP và số cổng do client code chỉ định;
    2. Ngắt liên kết TCP;
    3. Gửi truy vấn ở dạng chuỗi ký tự lên liên kết TCP;
    4. Đọc phản hồi từ liên kết TCP và chuyển thành chuỗi ký tự;
    5. Mỗi khi gửi truy vấn đi hoặc nhận phản hồi về sẽ lưu trữ lại thông tin vào hai mảng động để client có thể truy xuất truy vấn và phản hồi cuối cùng.
    6. Xử lý quy trình kết nối với FTP server;
    7. Xử lý quy trình xác thực để tạo kênh điều khiển;
    8. Xử lý quy trình đăng xuất khỏi server

    Client code là code sẽ sử dụng class này để tạo object và thực thi công việc cần thiết.

    Các phương thức còn lại có tác dụng lưu vết quá trình trao đổi thông tin giữa client và server để tiện theo dõi cách hoạt động của giao thức.

    Xây dựng lớp Program

    (1) Mở file Program.cs và viết code như sau:

    using System;
    using System.Net;
    namespace FtpProtocol {
        class Program {
            static void Help() {
                Console.WriteLine("Supported commands");
                Console.WriteLine("Account\t\tConnect\t\tLogin\t\tLogout\t\tQuit");
                Console.WriteLine("Dir\t\tMkDir\t\tRmDir\t\tCd\t\tCr");
                Console.WriteLine("Upload\t\tDownload");
                Console.WriteLine("--------------------------------------------");
            }
            static void Reset(ref FtpClient client) {
                client = new FtpClient {
                    ResponseListChangedHandler = s => { Console.WriteLine("S> {0}", s); },
                    CommandListChangedHandler = s => Console.WriteLine("C> {0}", s)
                };
                Console.Write("User: ");
                client.User = Console.ReadLine();
                Console.Write("Password: ");
                client.Password = Console.ReadLine();
                Console.Write("IP: ");
                client.Address = IPAddress.Parse(Console.ReadLine());
            }
            static void Main(string[] args) {
                Console.OutputEncoding = System.Text.Encoding.UTF8;
                Console.InputEncoding = System.Text.Encoding.UTF8;
                Console.WriteLine("Simple FTP client by MYPE. Type Help to get supported commands.");
                FtpClient client = new FtpClient();
                bool quit = false;
                string file, folder;
                while (!quit) {
                    Console.Write("fpt> ");
                    string cmd = Console.ReadLine();
                    try {
                        switch (cmd.ToUpper()) {
                            case "ACCOUNT":
                                Reset(ref client);
                                break;
                            case "QUIT":
                                quit = true;
                                break;
                            case "HELP":
                                Help();
                                break;
                            case "CONNECT":
                                client.Connect();
                                break;
                            case "LOGIN":
                                client.Login();
                                break;
                            case "LOGOUT":
                                client.Logout();
                                break;                        
                            default:
                                Console.WriteLine("Unknown command");
                                break;
                        }
                    } catch (Exception e) {
                        Console.WriteLine(e.Message);
                    }
                }
            }
        }
    }

    (2) Chạy thử chức năng kết nối, đăng nhập, đăng xuất.

    Cần chú ý:

    Thứ tự thực hiện thao tác như sau: account (nhập thông tin về tài khoản), connect (tạo liên kết TCP tới server), login (xác thực người dùng), logout (ngắt kết nối với server).

    Thông tin về tài khoản bao gồm tên người dùng, mật khẩu, và địa chỉ IP của server. Cách tạo tài khoản đã được hướng dẫn trong bài học Hệ thống phần mềm truyền file FTP.

    Thao tác với thư mục

    Trong phần này chúng ta sẽ bổ sung các tính năng hỗ trợ thao tác với thư mục, bao gồm tạo thư mục, xóa thư mục, đổi thư mục hiện hành, xem tên thư mục hiện hành.

    (1) Bổ sung code sau vào class FtpClient

    public void RemoveDirectory(string dir) {
        Command = string.Format("RMD {0}", dir);
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
    }
    public void CreateDirectory(string dir) {
        Command = string.Format("MKD {0}", dir);
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
    }
    public void ChangeDirectory(string dir) {
        Command = string.Format("CWD {0}", dir);
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
    }
    public void CurrentDirectory() {
        Command = "PWD";
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
    }

    (2) Bổ sung các case sau vào cấu trúc switch-case của phương thức Main()

    case "CR":
        client.CurrentDirectory();
        break;
    case "CD":
        Console.Write(">Go to folder: ");
        folder = Console.ReadLine();
        client.ChangeDirectory(folder);
        break;
    case "MKDIR":
        Console.Write(">New folder name: ");
        folder = Console.ReadLine();
        client.CreateDirectory(folder);
        break;
    case "RMDIR":
        Console.Write(">Folder to remove: ");
        folder = Console.ReadLine();
        client.RemoveDirectory(folder);
        break;

    Đến đây, bạn có thể dịch và chạy thử chương trình với các lệnh: cr (xem tên thư mục hiện hành), cd (đổi thư mục hiện hành), mkdir (tạo thư mục mới), rmdir (xóa thư mục).

    Lấy danh sách file và thư mục

    (1) Bổ sung các phương thức sau vào class FtpClient

    public void List() {
        Command = "PASV";
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
        if (Response.StartsWith("227 ")) {
            IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
            Command = "LIST";
            _writer.WriteLine(Command);
            TcpClient data_channel = new TcpClient();
            data_channel.Connect(server_data_endpoint);
            Response = _reader.ReadLine();
            if (Response.StartsWith("150 ")) {
                StreamReader sr = new StreamReader(data_channel.GetStream());
                Response = sr.ReadToEnd();
                Response = _reader.ReadLine();
                if (Response.StartsWith("226 ")) {
                    data_channel.Close();
                }
            }
        }    
    }
    IPEndPoint GetServerEndpoint(string response) {
        int start = response.IndexOf('(');
        int end = response.IndexOf(')');
        string substr = response.Substring(start + 1, end - start - 1);
        string[] octets = substr.Split(',');
        int port = int.Parse(octets[4]) * 256 + int.Parse(octets[5]);
        IPAddress address = new IPAddress(new byte[] { byte.Parse(octets[0]), byte.Parse(octets[1]), byte.Parse(octets[2]), byte.Parse(octets[3]) });
        return new IPEndPoint(address, port);
    }

    Phương thức này lấy danh sách file và thư mục từ FTP server theo phương thức thụ động, kết hợp lệnh PASV và LIST.

    Phương thức GetServerEndpoint() chịu trách nhiệm phân tích phản hồi của server trong trường hợp tạo kênh dữ liệu theo phương thức thụ động PASV. Phương thức này trả về một chuỗi có dạng (a, b, c, d, x, y), trong đó a, b, c, d là 4 octet của địa chỉ IP server, x và y là 2 octet để tính ra số cổng theo công thức x*256+y.

    (2) Bổ sung case sau vào Main()

    case "DIR":
        client.List();
        break;

    Xử lý tải file

    Tải file lên

    (1) Bổ sung phương thức sau vào lớp Ftpclient

    public void Download(string filename) {
        Command = "PASV";
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
        if (Response.StartsWith("227 ")) {
            IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
            Command = string.Format("RETR {0}", filename);
            _writer.WriteLine(Command);
            TcpClient data_channel = new TcpClient();
            data_channel.Connect(server_data_endpoint);
            Response = _reader.ReadLine();
            if (Response.StartsWith("150 ")) {
                NetworkStream ns = data_channel.GetStream();
                int blocksize = 1024;
                byte[] buffer = new byte[blocksize];
                int byteread = 0;
                lock (this) {
                    FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
                    while (true) {
                        byteread = ns.Read(buffer, 0, blocksize);
                        fs.Write(buffer, 0, byteread);
                        if (byteread == 0) {
                            break;
                        }
                    }
                    fs.Flush();
                    fs.Close();
                }
                Response = _reader.ReadLine();
                if (Response.StartsWith("226 ")) {
                    data_channel.Close();
                }
            }
        }
    }

    (2) Bổ sung case sau:

    case "DOWNLOAD":
        Console.Write(">File name: ");
        file = Console.ReadLine();
        client.Download(file);
        break;

    Tải file xuống

    (1) Bổ sung phương thức sau vào Ftpclient

    public void Upload(string filename) {
        Command = "PASV";
        _writer.WriteLine(Command);
        Response = _reader.ReadLine();
        if (Response.StartsWith("227 ")) {
            IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
            Command = string.Format("STOR {0}", filename);
            _writer.WriteLine(Command);
            TcpClient data_channel = new TcpClient();
            data_channel.Connect(server_data_endpoint);
            Response = _reader.ReadLine();
            if (Response.StartsWith("150 ")) {
                NetworkStream ns = data_channel.GetStream();
                int blocksize = 1024;
                byte[] buffer = new byte[blocksize];
                int byteread = 0;
                lock (this) {
                    FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
                    while (true) {
                        byteread = fs.Read(buffer, 0, blocksize);
                        ns.Write(buffer, 0, byteread);
                        if (byteread == 0) {
                            break;
                        }
                    }
                    ns.Flush();
                    ns.Close();
                }
                Response = _reader.ReadLine();
                if (Response.StartsWith("226 ")) {
                    data_channel.Close();
                }
            }
            data_channel.Close();
        }
    }

    (2) Bổ sung case sau:

    case "UPLOAD":
        Console.Write(">File name: ");
        file = Console.ReadLine();
        client.Upload(file);
        break;

    Chạy client với FileZilla server

    Đến đây bạn đã có thể chạy thử client vừa viết với FileZilla server vừa cấu hình. Kết quả chạy chương trình như sau:

    Bạn có thể chạy lệnh Help để xem các lệnh FTP được hỗ trợ:

    Bước 1. Chạy lệnh Account để đặt tài khoản vào client

    Chú ý, bạn cần tạo tài khoản trước khi đăng nhập. Cách tạo tài khoản đã được hướng dẫn trong bài học Hệ thống phần mềm truyền file FTP.

    Bước 2. Chạy lệnh connect để kết nối với server

    Bước 3. Chạy lệnh login để đăng nhập vào server

    Bước 4. Chạy lệnh Dir để xem các file và thư mục được chia sẻ

    Đến đây, bạn có thể thực hiện các lệnh làm việc với thư mục khác.

    Lưu ý rằng, các lệnh làm việc với thư mục là lệnh của người dùng. Nó không phải là lệnh của FTP. Mỗi lệnh của người dùng có thể tương ứng với 1 lệnh FTP, nhưng cũng có thể tương ứng với một chuỗi lệnh FTP phức tạp.

    Các lệnh người dùng được chúng ta tạo ra gần giống với lệnh của DOS / Shell.

    Nếu muốn kết thúc phiên làm việc và ngắt kết nối, bạn có thể sử dụng lệnh Logout. Nếu client không làm gì thì sau một khoảng thời gian, server cũng chủ động ngắt kết nối của kênh điều khiển.

    Mã nguồn đầy đủ của các file FtpClient.cs và Program.cs dưới đây

    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    namespace FtpProtocol {
        public class FtpClient {
            #region Properties
            TcpClient _controlSocket;
            StreamWriter _writer;
            StreamReader _reader;
            readonly BindingList<string> _responses = new BindingList<string>();
            readonly BindingList<string> _commands = new BindingList<string>();
            public string Command {
                get => _commands.Last();
                private set {
                    if (!string.IsNullOrEmpty(value)) {
                        _commands.Add(value);
                    }
                }
            }
            public string Response {
                get => _responses.Last();
                private set {
                    if (!string.IsNullOrEmpty(value)) {
                        _responses.Add(value);
                    }
                }
            }
            public IPAddress Address { get; set; }
            public string User { get; set; }
            public string Password { get; set; }
            public bool Connected => _controlSocket.Connected;
            public bool LoggedOn { get; private set; }
            #endregion
            #region Commands  
            public void Connect() {
                _responses.Clear();
                _commands.Clear();
                _controlSocket = new TcpClient();
                if (!_controlSocket.Connected) {
                    _controlSocket.Connect(Address, 21);
                    if (_controlSocket.Connected) {
                        _responses.ListChanged -= Responses_ListChanged;
                        _responses.ListChanged += Responses_ListChanged;
                        _commands.ListChanged -= Commands_ListChanged;
                        _commands.ListChanged += Commands_ListChanged;
                        _reader = new StreamReader(_controlSocket.GetStream());
                        _writer = new StreamWriter(_controlSocket.GetStream()) { AutoFlush = true };
                        StringBuilder sb = new StringBuilder();
                        Response = _reader.ReadLine();
                        if (Response.StartsWith("220-")) {
                            while (true) {
                                Response = _reader.ReadLine();
                                if (Response.StartsWith("220 ")) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            public void Login() {
                Command = string.Format("USER {0}", User);
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
                if (Response.StartsWith("331 ")) {
                    Command = string.Format("PASS {0}", Password);
                    _writer.WriteLine(Command);
                    Response = _reader.ReadLine();
                    LoggedOn = true;
                }
            }
            public void Logout() {
                if (_controlSocket.Connected && LoggedOn) {
                    Command = "QUIT";
                    _writer.WriteLine(Command);
                    Response = _reader.ReadLine();
                    if (Response.StartsWith("221 ")) {
                        LoggedOn = false;
                        _controlSocket.Close();
                    }
                }
            }
            public void RemoveDirectory(string dir) {
                Command = string.Format("RMD {0}", dir);
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
            }
            public void CreateDirectory(string dir) {
                Command = string.Format("MKD {0}", dir);
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
            }
            public void ChangeDirectory(string dir) {
                Command = string.Format("CWD {0}", dir);
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
            }
            public void CurrentDirectory() {
                Command = "PWD";
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
            }
            public void Upload(string filename) {
                Command = "PASV";
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
                if (Response.StartsWith("227 ")) {
                    IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
                    Command = string.Format("STOR {0}", filename);
                    _writer.WriteLine(Command);
                    TcpClient data_channel = new TcpClient();
                    data_channel.Connect(server_data_endpoint);
                    Response = _reader.ReadLine();
                    if (Response.StartsWith("150 ")) {
                        NetworkStream ns = data_channel.GetStream();
                        int blocksize = 1024;
                        byte[] buffer = new byte[blocksize];
                        int byteread = 0;
                        lock (this) {
                            FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
                            while (true) {
                                byteread = fs.Read(buffer, 0, blocksize);
                                ns.Write(buffer, 0, byteread);
                                if (byteread == 0) {
                                    break;
                                }
                            }
                            ns.Flush();
                            ns.Close();
                        }
                        Response = _reader.ReadLine();
                        if (Response.StartsWith("226 ")) {
                            data_channel.Close();
                        }
                    }
                    data_channel.Close();
                }
            }
            public void Download(string filename) {
                Command = "PASV";
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
                if (Response.StartsWith("227 ")) {
                    IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
                    Command = string.Format("RETR {0}", filename);
                    _writer.WriteLine(Command);
                    TcpClient data_channel = new TcpClient();
                    data_channel.Connect(server_data_endpoint);
                    Response = _reader.ReadLine();
                    if (Response.StartsWith("150 ")) {
                        NetworkStream ns = data_channel.GetStream();
                        int blocksize = 1024;
                        byte[] buffer = new byte[blocksize];
                        int byteread = 0;
                        lock (this) {
                            FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);
                            while (true) {
                                byteread = ns.Read(buffer, 0, blocksize);
                                fs.Write(buffer, 0, byteread);
                                if (byteread == 0) {
                                    break;
                                }
                            }
                            fs.Flush();
                            fs.Close();
                        }
                        Response = _reader.ReadLine();
                        if (Response.StartsWith("226 ")) {
                            data_channel.Close();
                        }
                    }
                }
            }
            public void List() {
                Command = "PASV";
                _writer.WriteLine(Command);
                Response = _reader.ReadLine();
                if (Response.StartsWith("227 ")) {
                    IPEndPoint server_data_endpoint = GetServerEndpoint(Response);
                    Command = "LIST";
                    _writer.WriteLine(Command);
                    TcpClient data_channel = new TcpClient();
                    data_channel.Connect(server_data_endpoint);
                    Response = _reader.ReadLine();
                    if (Response.StartsWith("150 ")) {
    
                        StreamReader sr = new StreamReader(data_channel.GetStream());
                        Response = sr.ReadToEnd();
                        Response = _reader.ReadLine();
                        if (Response.StartsWith("226 ")) {
                            data_channel.Close();
                        }
                    }
                }
            }
            IPEndPoint GetServerEndpoint(string response) {
                int start = response.IndexOf('(');
                int end = response.IndexOf(')');
                string substr = response.Substring(start + 1, end - start - 1);
                string[] octets = substr.Split(',');
                int port = int.Parse(octets[4]) * 256 + int.Parse(octets[5]);
                IPAddress address = new IPAddress(new byte[] { byte.Parse(octets[0]), byte.Parse(octets[1]), byte.Parse(octets[2]), byte.Parse(octets[3]) });
                return new IPEndPoint(address, port);
            }
            #endregion
            #region Logging
            public Action<string> ResponseListChangedHandler { get; set; }
            public Action<string> CommandListChangedHandler { get; set; }
            private void Responses_ListChanged(object sender, ListChangedEventArgs e) {
                if (e.ListChangedType == ListChangedType.ItemAdded) {
                    string response = _responses[e.NewIndex];
                    ResponseListChangedHandler?.Invoke(response);
                }
            }
            private void Commands_ListChanged(object sender, ListChangedEventArgs e) {
                if (e.ListChangedType == ListChangedType.ItemAdded) {
                    string command = _commands[e.NewIndex];
                    CommandListChangedHandler?.Invoke(command);
                }
            }
            #endregion
        }
    }
    
    using System;
    using System.Net;
    namespace FtpProtocol {
        class Program {
            static void Help() {
                Console.WriteLine("Supported commands");
                Console.WriteLine("Account\t\tConnect\t\tLogin\t\tLogout\t\tQuit");
                Console.WriteLine("Dir\t\tMkDir\t\tRmDir\t\tCd\t\tCr");
                Console.WriteLine("Upload\t\tDownload");
                Console.WriteLine("--------------------------------------------");
            }
            static void Reset(ref FtpClient client) {
                client = new FtpClient {
                    ResponseListChangedHandler = s => { Console.WriteLine("S> {0}", s); },
                    CommandListChangedHandler = s => Console.WriteLine("C> {0}", s)
                };
                Console.Write("User: ");
                client.User = Console.ReadLine();
                Console.Write("Password: ");
                client.Password = Console.ReadLine();
                Console.Write("IP: ");
                client.Address = IPAddress.Parse(Console.ReadLine());
            }
            static void Main(string[] args) {
                Console.OutputEncoding = System.Text.Encoding.UTF8;
                Console.InputEncoding = System.Text.Encoding.UTF8;
                Console.WriteLine("Simple FTP client by MYPE. Type Help to get supported commands.");
                FtpClient client = new FtpClient();
                bool quit = false;
                string file, folder;
                while (!quit) {
                    Console.Write("fpt> ");
                    string cmd = Console.ReadLine();
                    try {
                        switch (cmd.ToUpper()) {
                            case "ACCOUNT":
                                Reset(ref client);
                                break;
                            case "QUIT":
                                quit = true;
                                break;
                            case "HELP":
                                Help();
                                break;
                            case "CONNECT":
                                client.Connect();
                                break;
                            case "LOGIN":
                                client.Login();
                                break;
                            case "LOGOUT":
                                client.Logout();
                                break;
                            case "CR":
                                client.CurrentDirectory();
                                break;
                            case "CD":
                                Console.Write(">Go to folder: ");
                                folder = Console.ReadLine();
                                client.ChangeDirectory(folder);
                                break;
                            case "MKDIR":
                                Console.Write(">New folder name: ");
                                folder = Console.ReadLine();
                                client.CreateDirectory(folder);
                                break;
                            case "RMDIR":
                                Console.Write(">Folder to remove: ");
                                folder = Console.ReadLine();
                                client.RemoveDirectory(folder);
                                break;
                            case "DIR":
                                client.List();
                                break;
                            case "DOWNLOAD":
                                Console.Write(">File name: ");
                                file = Console.ReadLine();
                                client.Download(file);
                                break;
                            case "UPLOAD":
                                Console.Write(">File name: ");
                                file = Console.ReadLine();
                                client.Upload(file);
                                break;
                            default:
                                Console.WriteLine("Unknown command");
                                break;
                        }
                    } catch (Exception e) {
                        Console.WriteLine(e.Message);
                    }
                }
            }
        }
    }
    

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    Kết luận

    Trong bài học này chúng ta đã vận dụng các kỹ thuật lập trình socket và kỹ thuật lập trình C# nâng cao để xây dựng một FTP client đơn giản.

    Có thể thấy rằng, giao thức FTP tương đối phức tạp về quy trình làm việc. Tuy nhiên, nếu tuân thủ đầy đủ quy tắc của giao thức này, chương trình của chúng ta hoàn toàn có thể tương tác được với chương trình server khác không do chúng ta phát triển.

    Theo dõi
    Thông báo của
    guest

    0 Thảo luận
    Phản hồi nội tuyến
    Xem tất cả bình luận