SSブログ

VB.NETでFTPダウンロードを実装する方法 [プログラミング]

VB.NETでFTPダウンロードを実装する方法について説明する。

ここで説明するFTPダウンロードは、FireWallサーバにログインした後、プロキシサーバを経由して目的のサーバに接続する形式である。
プロキシを経由しないFTP接続のサンプルはたくさんあるようなので、ここではプロキシ経由のサンプルを紹介する。

FTPクライアントにはSystem.Net.Sockets名前空間のSocketクラスを使用するしSystem.Net名前空間を多用するので、System.Netをインポートしておくと良い。

簡単に処理の流れは以下の通り。

1.Socketクラスのインスタンスの生成
2.FireWallサーバにログイン
3.プロキシサーバを経由して目的サーバにログイン
4.転送モード、送受信ポートの設定
5.ファイルのダウンロード
6.サーバとの接続を閉じる

では、その詳細を説明していこう。

1.Socketクラスのインスタンスの生成

Socketクラスのインスタンスの生成は「アドレスファミリ、ソケットタイプおよびプロトコル」を指定してインスタンスの初期化を行う。
アドレスファイミリは"InterNetwork"、ソケットタイプは"Stream"、そしてプロトコルは"Tcp"を指定する。


2.FireWallサーバにログイン

FireWallサーバにログインするための手順は以下の通り。
①IPEndPointクラス(System.Net名前空間)のインスタンスの生成
 FireWallサーバに接続するためにIPEndPointクラスのインスタンスを生成して、接続先のホストとポートの情報を格納する。
 IPアドレスにはDnsクラスのGetHostEntryメソッドにFireWallサーバの名前(名前解決ができていれば)またはIPアドレスを指定して実行した結果のAddressListプロパティの0番目要素を、ポート番号には21を指定してインスタンスの初期化を行う。
②FireWallサーバに接続
 SocketクラスのインスタンスのConnectメソッドに先ほど生成したIPEndPointクラスのインスタンスを指定して実行してFireWallサーバに接続する。
③FireWallサーバにログイン
 SocketクラスのインスタンスのSendメソッドに"USER [FireWallサーバの名前またはIPアドレス]"を指定して実行する。
 ※Sendメソッドの詳しい実装方法は後程説明する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
 ※Receiveメソッドの詳しい実装方法は後程説明する。
④FireWallユーザのパスワード送信
 SocketクラスのインスタンスのSendメソッドに"PASS [FireWallユーザのパスワード]"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。

以上でFireWallサーバへのログインは完了である。


3.プロキシサーバを経由して目的サーバにログイン

プロキシサーバ経由で目的サーバにログインするための手順は以下の通り。
①目的サーバにログイン
 SocketクラスのインスタンスのSendメソッドに"USER [ユーザ名]@[目的サーバの名前またはIPアドレス]@[プロキシサーバの名前またはIPアドレス]"を指定して実行する。
 因みにプロキシサーバを経由しない場合は"USER [ユーザ名]@[目的サーバの名前またはIPアドレス]"を指定する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②目的サーバのログインパスワード送信
 SocketクラスのインスタンスのSendメソッドに"PASS [目的サーバのログインパスワード]"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。

以上で目的サーバへのログインは完了である。


4.転送モード、送受信ポートの設定

転送モード、送受信ポートの設定手順は以下の通り。
①転送モードをASCIIに設定
 SocketクラスのインスタンスのSendメソッドに"TYPE A"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②クライアントのIPアドレスの取得
 IPAddressクラス(System.Net名前空間)のインスタンスにDnsクラスのGetHostAddressesメソッドの実行結果の1番目要素を設定する。
 DnsクラスのGetHostAddressesメソッドにはDnsクラスのGetHostNameメソッドの実行結果を指定する。
③Tcpリスナーの生成
 TcpListenerクラス(System.Net.Sockets名前空間)のインスタンスを生成する。
 IPアドレスには先ほど生成したIPAddressクラスのインスタンスを、ポートには0を指定して初期化する。
④Tcpリスナーの受信接続要求開始
 TcpListenerクラスのインスタンスのStartメソッドを実行して受信接続要求の待機を開始する。
⑤クライアントの受信ポートの取得
 TcpListenerクラスのインスタンスのLocalEndpointプロパティをIPEndPointに型変換し、そのPortプロパティの値を取得する。
⑥送受信ポートの設定
 SocketクラスのインスタンスのSendメソッドに"PROT [IPAddressクラスのインスタンスを文字列変換した値の"."(ピリオド)を","(カンマ)に変換した値],[クライアント受信ポート番号÷256の商],[クライアント受信ポート番号÷256の剰余]"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。

以上で転送モード、送受信ポートの設定は完了である。


5.ファイルのダウンロード

ファイルのダウンロードの手順は以下の通り。
①ダウンロードを要求するファイルの送信
 SocketクラスのインスタンスのSendメソッドに"RETR [ダウンロードするファイルのフルパス]"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②ファイルのダウンロード
 Socketクラス(System.Net.Sockets名前空間)のインスタンスを生成し、TcpListenerクラスのインスタンスのAcceptSocketプロパティの値を設定する。
 FileStreamクラス(IO名前空間)のインスタンスを生成し、ダウンロード先ファイルのフルパスで初期化する。
 受信データが0バイトになるまでSocketクラスのインスタンスのReceiveメソッドで取得したバイト型の配列の値をFileStreamクラスのインスタンスのWriteメソッドでファイルに出力する。
 ファイルの受信が完了したらSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
③各インスタンスのクローズ
 FileStreamクラスのインスタンスのCloseメソッドを実行してダウンロード先ファイルを閉じる。
 SocketクラスのインスタンスのCloseメソッドを実行してファイル受信ソケットを閉じる。
 TcpListenerクラスのインスタンスのStopメソッドを実行してTcpリスナーを閉じる。

以上でファイルのダウンロードは完了である。


6.サーバとの接続を閉じる

サーバとの接続終了の手順は以下の通り。
①サーバとの接続を閉じる
 SocketクラスのインスタンスのSendメソッドに"QUIT"を指定して実行する。
 続いてSocketクラスのインスタンスのReceiveメソッドを実行してサーバからの応答を受信する。
②ソケット接続の終了
 SocketクラスのインスタンスのShutdownメソッドを実行してソケットの送受信を終了する。
 SocketクラスのインスタンスのCloseメソッドを実行してソケットの接続を閉じる。


上記の手順でプロキシサーバ経由のFTPダウンロードが行える。
もっとも、プロキシサーバ経由か否かは目的サーバへのログインが異なるだけ(そこが肝心な部分だが…)で、その他はプロキシ経由か否かに関係なく同じ手順である。


説明を保留していたSocketクラスのインスタンスのSendメソッドとReceiveメソッドについて以下に説明する。

1.Sendメソッド

SocketクラスのインスタンスのSendメソッドでコマンドを送信する手順は以下の通り。
①送信するコマンドをバイト列に変換
 送信するコマンドをASCIIEncodingで初期化したEncodingクラス(Text名前空間)のインスタンスのGetBytesメソッドを実行して、ASCIIのバイト列に変換する。
②コマンドの送信
 SocketクラスのインスタンスのSendメソッドに送信するコマンドのASCIIのバイト列とその長さを指定して実行する。


2.Receiveメソッド

SocketクラスのインスタンスのReceiveメソッドでサーバからの応答を受信する手順は以下の通り。
①サーバからの応答の受信
 SocketクラスのインスタンスのAvailableプロパティの値>0の間、以下の処理を繰り返す。
  SocketクラスのインスタンスのReceiveメソッドを実行してバイト列にデータを受信する。
  受信したバイト列のデータをUTF8Encodingで初期化したEncodingクラス(Text名前空間)のインスタンスのGetStringメソッドを実行して、文字列に変換する。

 

以下は、上記FTPダウンロード手順のサンプルコード(抜粋)である。
フォームには以下のテキストボックスとボタンが配置されているものとする。
 ・FireWallサーバ名
 ・FireWallユーザ名
 ・FireWallパスワード
 ・プロキシサーバ名
 ・ホストサーバ名
 ・ホストサーバユーザ名
 ・ホストサーバパスワード
 ・ダウンロード元パス
 ・ダウンロード元ファイル名
 ・ダウンロード先パス
 ・ダウンロード先ファイル名
 ・実行ログ
 ・接続ボタン

フォームの各テキストボックスに値を入力し、接続ボタンを押下するとFTPダウンロードが始まり、サーバとの送受信内容が実行ログテキストボックスに出力される仕様である。
以下のサンプルではサーバへのコマンド送信後、サーバからの応答メッセージを受信する際にコマンドエラーのハンドリングを行っていないが、エラーハンドリングを行うのであれば受信データを文字列変換した後にエラーコードもしくはメッセージが含まれていないかをチェックすると良い。
因みにこのサンプルの接続先サーバはLinuxサーバである。

------------------------------------------------------------

    Private gstrFireWallServer As String
    Private gstrFireWallUser As String
    Private gstrFireWallPassword As String
    Private gstrHostProxy As String
    Private gstrHostServer As String
    Private gstrHostUser As String
    Private gstrHostPassword As String
    Private gstrHostPath As String
    Private gstrHostFile As String
    Private gstrClientPath As String
    Private gstrClientFile As String
    Private goClient As Sockets.Socket
    Private goEncASC As Text.Encoding = New Text.ASCIIEncoding
    Private goEncU8 As Text.Encoding = New Text.UTF8Encoding

    Private Sub btnConnect_Click(sender As Object, e As System.EventArgs) Handles btnConnect.Click
        '********************************
        '接続ボタン押下時の処理
        '********************************

        '各種ユーザ入力情報の取得
        gstrFireWallServer = txtFireWallServer.Text.Trim
        gstrFireWallUser = txtFireWallUser.Text.Trim
        gstrFireWallPassword = txtFireWallPassword.Text.Trim
        gstrHostProxy = txtHostProxy.Text.Trim
        gstrHostServer = txtHostServer.Text.Trim
        gstrHostUser = txtHostUser.Text.Trim
        gstrHostPassword = txtHostPassword.Text.Trim
        gstrHostPath = txtHostPath.Text.Trim
        gstrHostFile = txtHostFile.Text.Trim
        gstrClientPath = txtClientPath.Text.Trim
        gstrClientFile = txtClientFile.Text.Trim

        '指定ファイルをFTPでサーバより取得
        If fncGetFileForFtp() = False Then
            Me.fncMessageput("ファイルの取得に失敗しました。", MsgBoxStyle.Exclamation)
        Else
            Me.fncMessageput("ファイルの取得が完了しました。", MsgBoxStyle.Information)
        End If

    End Sub

    Private Function fncGetFileForFtp() As Boolean
        '********************************
        'FTPで指定サーバより指定ファイルをダウンロード
        '
        '   戻り値         :   True:正常、False:異常
        '
        '********************************

        Dim blnReturn As Boolean
        Dim blnConnect As Boolean = False

        Try
            txtLog.Clear()
            'ソケットオブジェクトの生成
            goClient = New Sockets.Socket(Sockets.AddressFamily.InterNetwork, Sockets.SocketType.Stream, Sockets.ProtocolType.Tcp)
            'ホスト(FireWall)の接続先とポートを生成
            Dim objFwIp As IPEndPoint = New IPEndPoint(Dns.GetHostEntry(gstrFireWallServer).AddressList(0), 21)
            'ホストに接続
            goClient.Connect(objFwIp)
            'FireWallにログイン
            If fncSendCommand("USER " & gstrFireWallUser) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If
            blnConnect = True
            If fncSendCommand("PASS " & gstrFireWallPassword, gstrFireWallPassword) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If
            'サーバにログイン
            If fncSendCommand("USER " & gstrHostUser & "@" & gstrHostServer & "@" & gstrHostProxy) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If
            If fncSendCommand("PASS " & gstrHostPassword, gstrHostPassword) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If

            '転送モードをASCIIに設定
            If fncSendCommand("TYPE A") = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If

            'クライアントのIPアドレスと受信ポートを設定
            Dim objMyIp As IPAddress = Dns.GetHostAddresses(Dns.GetHostName())(1)
            Dim objListener As Sockets.TcpListener = New Sockets.TcpListener(objMyIp, 0)
            objListener.Start()
            Dim intMyPort As Integer = CInt(DirectCast(objListener.LocalEndpoint, IPEndPoint).Port.ToString())

            '送受信ポートを設定
            If fncSendCommand("PORT " & objMyIp.ToString.Replace(".", ",") & "," & (intMyPort \ 256) & "," & (intMyPort Mod 256)) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If

            'ダウンロードを要求するファイルを送信
            If fncSendCommand("RETR " & gstrHostPath & "/" & gstrHostFile) = False Then
                Exit Try
            End If
            If fncReceiveData() = False Then
                Exit Try
            End If

            'ファイルをダウンロード
            Dim objSoc As Sockets.Socket = objListener.AcceptSocket
            Dim objStream As New IO.FileStream(IO.Path.Combine(gstrClientPath, gstrClientFile), IO.FileMode.Create, IO.FileAccess.Write)
            Dim bytBuffer(1023) As Byte
            Dim datStart As Date = Now()
            Do
                Dim intSize As Integer = objSoc.Receive(bytBuffer)
                If intSize = 0 Then
                    Exit Do
                End If
                objStream.Write(bytBuffer, 0, intSize)
            Loop
            Dim datSpan As TimeSpan = Now() - datStart
            If fncReceiveData() = False Then
                Exit Try
            End If
            Dim strMsg As String = "ダウンロードは正常に終了しました。 (" & CInt(datSpan.TotalSeconds).ToString & "SEC. " & _
                                    CInt(objStream.Length / datSpan.TotalSeconds).ToString & "B/S)"
            Me.subPutLog(strMsg)
            objStream.Close()
            objSoc.Close()
            objListener.Stop()

            blnReturn = True

        Catch ex As Exception
            Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
            blnReturn = False

        Finally
            If blnConnect = True Then
                '接続を閉じる
                Call fncSendCommand("QUIT")
                Call fncReceiveData()
                goClient.Shutdown(Sockets.SocketShutdown.Both)
                goClient.Close()
            End If
            goClient.Dispose()
            goClient = Nothing

        End Try

        Return blnReturn

    End Function

    Private Function fncReceiveData() As Boolean
        '********************************
        'サーバから応答メッセージを取得
        '
        '   戻り値          :   True:正常、False:異常
        '
        '********************************

        Dim blnReturn As Boolean
        Dim bytData(255) As Byte
        Dim intLen As Integer
        Dim strData As String

        Try
            Do While goClient.Available > 0
                'サーバからの応答を受信
                intLen = goClient.Receive(bytData)
                '受信したメッセージのバイト列を文字列に変換
                strData = goEncU8.GetString(bytData, 0, intLen)
                Me.subPutLog(strData)
                Threading.Thread.Sleep(250)
            Loop

            blnReturn = True

        Catch ex As Exception
            Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
            blnReturn = False

        End Try

        Return blnReturn

    End Function

    Private Function fncSendCommand(ByVal pCommand As String, Optional ByVal pPassword As String = "") As Boolean
        '********************************
        'サーバにコマンドを送信
        '
        '   pCommand        :   送信コマンド文字列
        '   pPassword       :   パスワード文字列
        '
        '   戻り値          :   True:正常、False:異常
        '
        '********************************

        Dim blnReturn As Boolean

        Try
            Me.subPutLog(">" & pCommand, pPassword)

            '送信するコマンドをASCIIのバイト列に変換
            pCommand = pCommand & ControlChars.CrLf
            Dim bytCommand() As Byte = goEncASC.GetBytes(pCommand)

            'コマンドを送信
            goClient.Send(bytCommand, bytCommand.Length, Sockets.SocketFlags.None)
            Threading.Thread.Sleep(250)

            blnReturn = True

        Catch ex As Exception
            Me.fncMessageput(ex.Message, MsgBoxStyle.Exclamation)
            blnReturn = False

        End Try

        Return blnReturn

    End Function

------------------------------------------------------------

 


タグ:Socket FTP VB.NET
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。