September 16, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Write an FTP Client with VB.NET to Bridge Legacy Software

  • October 20, 2004
  • By Paul Kimmel
  • Send Email »
  • More Articles »

Implementing the FTP Client

Now that you have verified that we have an FTP server to test your client with, you can begin writing the FTP client. The partial client in this example involves creating a socket and an IPEndPoint, connecting to and disconnecting from the server, and authenticating. You can implement everything else, such as obtaining a file list or transferring files, by building on the example here. (In an upcoming article, I will implement a Windows Forms presentation layer and implement additional FTP features.)

Connecting to an FTP Server

To connect to an FTP server, you need to know three things:

  1. The IP Address or URL of the host
  2. The port
  3. The protocol

FTP is a TCP service (that's your protocol). FTP uses port 21 for command and port 20 for data (Port 21 is your port). Because you are testing on your own workstation, your IP address is 127.0.0.1 (equivalent to the URL localhost).

Note: In a commercial quality implementation, you would probably externalize the FTP configuration data. You can do this using a Web.config or App.config file and the appSettings section, or you can write an IConfigSectionHandler and create your own .config block. Using the <appSettings> is good enough, but using an IConfigSectionHandler is much cooler. Refer to my previous article, "Objectify an XML Node with an IConfigSectionHandler," for an example of implementing a custom XML section handler.

Knowing which server you want to connect to leaves just a few lines of code to write. In addition to writing the Connect behavior, you need to import some namespaces to make referring to sockets and endpoints shorter, and you can add fields and constructors to hold information about the server to which you would like to connect. Listing 1 contains the class stub, imports statement, a constructor, and the Connect method.

Listing 1: The Class Stub and Connect Method.

Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Text.ASCIIEncoding
Imports System.Net.Sockets
Imports System.Configuration
Imports System.Resources


Public Class FtpClient

    Private FRemoteHost     As String  = "127.0.0.1"
    Private FRemotePath     As String  = "."
    Private FRemoteUser     As String  = "anonymous"
    Private FRemotePassword As String  = "anonymous@nowhere.com"
    Private FRemotePort     As Integer = 21
    Private clientSocket    As Socket  = Nothing
    Private response        As String

    Public Sub New()
    End Sub

    Public Sub New(ByVal remoteHost As String, ByVal remotePath _
                   As String, _
    ByVal remoteUser As String, ByVal remotePassword As String, _
    ByVal remotePort As Integer)
        FRemoteHost     = remoteHost
        FRemotePath     = remotePath
        FRemoteUser     = remoteUser
        FRemotePassword = remotePassword
        FRemotePort     = remotePort
    End Sub


    Public Sub Connect()
        clientSocket = New Socket(AddressFamily.InterNetwork, _
        SocketType.Stream, ProtocolType.Tcp)
        Dim endpoint As IPEndPoint = _
          New IPEndPoint(Dns.Resolve(FRemoteHost).AddressList(0), _
                         FRemotePort)

        Try
            clientSocket.Connect(endpoint)
        Catch ex As Exception
            Throw New IOException("Connect failed", ex)
        End Try

        ReadResponse()
    End Sub

You can look up information about each of the imported namespaces in the help documentation. The fields represent information about the server, an instance of the Socket class, and a field to hold data you received from the FTP server. The parameterized constructor (Sub New) initializes the server fields. You also provide default values for testing. (If you implemented an IconfigSectionHandler, then you could code the default constructor to read the server fields from a .config file.)

The method you want is Connect. Connect creates an instance of a socket. The parameters you use are needed for FTP, but you can use the same socket class to connect using other protocols like UDP (User Datagram Protocol) or IPX (Internetwork Packet Exchange). (UDP is commonly used in games to send data without waiting for a response, and IPX is an older Novell netware protocol that is also commonly used in older multiplayer games and with Novell networks.)

Next, you need an IPEndPoint, which represents an IP address and port. (All of this information is easily accessible in the integrated help documentation and online, so I will leave it to you to explore further.)

Finally, you call Socket.Connect, passing the endpoint. If you receive an exception, something went wrong. Otherwise, you are ready to start sending and receiving data, which you implement as ReadResponse in your FTP client.

Reading Server Response

For each interaction with the server, you get a response. The response comes back in the form of a string. In general, the string contains a three-digit code and some text, and for FTP requests such as GET you might get some data such as the requested file. You implement ReadResponse to try to read a complete response 512 bytes at a time and convert this response into an event.

ReadResponse is implemented as a method that invokes a second method called ReadLine and then broadcasts the response to any listeners. Listeners are instances of event handlers. Because the data from the server may come back in multi-line responses, ReadLine can recur. Add the three methods in Listing 2 to the FTPClient class from Listing 1.

Listing 2: Reading Responses from the FTP Server

    Private Sub ReadResponse()
        Dim reply As String = ReadLine()
        'Figure out the response and raise that event
        BroadcastResponse(reply)
    End Sub

    Private Function ReadLine() As String
        Return ReadLine(False)
    End Function

    Private Function ReadLine(ByVal clearResponse As Boolean) _
            As String
        Const EndLine As Char = "\n"
        Const BUFFER_SIZE As Integer = 512

        Dim data As String = ""
        Dim buffer(BUFFER_SIZE) As Byte
        Dim bytesRead As Integer = 0

        If (clearResponse = True) Then response = String.Empty

        While (True)
            Array.Clear(buffer, 0, BUFFER_SIZE)
            bytesRead = clientSocket.Receive(buffer, _
                                             buffer.Length, 0)
            data += ASCII.GetString(buffer, 0, bytesRead)

            If (bytesRead < buffer.Length) Then Exit While
        End While

        Dim parts() As String = data.Split(EndLine)

        If (parts.Length > 2) Then
            response = parts(parts.Length - 2)
        Else
            response = parts(0)
        End If

        If (response.Substring(3, 1).Equals(" ") = False) Then
            Return ReadLine(False)
        End If

        Return response

    End Function

When you constructed the Socket, you initialized it as a two-way byte stream with the argument SocketType.Stream. The main read loop—the While..End While loop—receives up to 512 bytes at a time. If the bytes read are less than 512, you stop reading. If more bytes are present, indicated loosely by the somewhat arbitrary presence of a space, we recurse; otherwise, we return the response to ReadResponse. ReadResponse calls a method named BroadcastResponse.





Page 2 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel