- PR -

VB.NET ActiveモードでのFTP処理について

1
投稿者投稿内容
かえで
常連さん
会議室デビュー日: 2004/09/16
投稿数: 38
投稿日時: 2005-07-11 21:15
いつもお世話になっております。

VB.NETで簡易FTPツールを作成しております。
備わっている機能は、「リストの一覧表示/ファイルのUPLOAD/DOWNLOAD」と単純なものです。
Passiveモードで処理を行う場合であれば、ネット上にサンプルがたくさんありましたので容易に開発できました。
しかしActiveモードで処理を行う場合のサンプルが少なく困っております。

【環境】
VB.NET FrameWork1.1
Windows2003Server

【問題点】
Activeモードでデータコネクションを張りLIST等の要求を発行したはずなのに、TcpListener.Pendingメソッドにてfalseが帰ってくることがよくある。
(※何回かFTPサーバーに接続/切断を繰り返す)

【ソース】
@FTPサーバーへの接続
 mclsCtrlConSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
 lclsCtrlCon = New IPEndPoint(Dns.Resolve("サーバー名").AddressList(0), 21)
 mclsCtrlConSocket.Connect(lclsCtrlCon)

 〜〜〜〜〜接続判定処理は省略

Aデータコネクションの確立
 Dim lipAddress As IPAddress = Dns.Resolve(Dns.GetHostName()).AddressList(0)
 testtcpListener = New TcpListener(lipAddress, 0)

 testtcpListener.Start()

 lclsDataConIPEP = CType(testtcpListener.LocalEndpoint, IPEndPoint)
 lsubSendCommand("PORT " & PortParam(lclsDataConIPEP.Port))

 Private Function PortParam(ByVal port As Integer) As String
  Dim localhost As IPHostEntry = Dns.Resolve(Dns.GetHostName())
  Dim IPAddress As IPAddress = localhost.AddressList(0)
  Dim address As String = IPAddress.ToString().Replace(".", ",")

  Dim i1 As Integer = CInt(port / 256)
  Dim i2 As Integer = port Mod 256

  Return address + "," + i1.ToString() + "," + i2.ToString()

BLIST命令発行
  lbytCommandBytes = ASCII.GetBytes("LIST /TEST")
  mclsCtrlConSocket.Send(lbytCommandBytes, lbytCommandBytes.Length, 0)

C処理判定
 if testtcpListener.Pending then ※←-----ここでfalseになる
  lclsDataConSocket = testtcpListener.AcceptSocket()
 End If

D終了処理
 testtcpListener.Stop()
 testtcpListener = Nothing

 lclsDataConSocket.Shutdown(SocketShutdown.Both)
 lclsDataConSocket.Close()
 lclsDataConSocket = Nothing

途中のソースは省略しましたが、大まかな流れはこのようになっております。

説明で分かりにくい所があるとは思いますが、ご存知の方がいらっしゃったらご教授して頂きたく思います。
宜しくお願い致します。

甕星
ぬし
会議室デビュー日: 2003/03/07
投稿数: 1185
お住まい・勤務地: 湖の見える丘の上
投稿日時: 2005-07-12 06:52
引用:

かえでさんの書き込み (2005-07-11 21:15) より:
BLIST命令発行
  lbytCommandBytes = ASCII.GetBytes("LIST /TEST")
  mclsCtrlConSocket.Send(lbytCommandBytes, lbytCommandBytes.Length, 0)

C処理判定
 if testtcpListener.Pending then ※←-----ここでfalseになる
  lclsDataConSocket = testtcpListener.AcceptSocket()
 End If


FTP Serverにコマンドを発行した後、応答が帰ってくるまでには多少の時間がかかります。コマンド発行後、直ちに接続要求の有無を確認して処理していたのでは、Server側での処理が早かったときは動作するかもしれませんが、ちょっと時間がかかっている場合にはPendingでFalseが帰ってくるでしょうね。
以下の様にPendingがTrueを返すまで、繰り返してください。
コード:
Do Until testtcpListener.Pending
    Thread.Sleep(100)
Loop
lclsDataConSocket = testtcpListener.AcceptSocket()


かえで
常連さん
会議室デビュー日: 2004/09/16
投稿数: 38
投稿日時: 2005-07-12 09:58
引用:

甕星さんの書き込み (2005-07-12 06:52) より:
コード:
Do Until testtcpListener.Pending
    Thread.Sleep(100)
Loop
lclsDataConSocket = testtcpListener.AcceptSocket()





なるほど、応答までの時間を考慮するべきなんですね。
指摘くださった方法で試してみることにします。

甕星様、ありがとうございました。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-07-12 11:49
TcpListner.AcceptSocket/AcceptTcpClientメソッドは、接続要求がキューにない間はスレッドをブロックします。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpref/html/frlrfsystemnetsocketstcplistenerclasstopic.asp
ですからこの場合わざわざPendingで接続要求をチェックする必要もないでしょう。
そのままAcceptしてやれば繋がるまで待ってくれます。
かえで
常連さん
会議室デビュー日: 2004/09/16
投稿数: 38
投稿日時: 2005-07-12 13:09
Hongliang様
msdn読ませて頂きました。
スレッドをブロックするとは気づきませんでした。。。

そこで今度はPendingをはずして実行しました。
LIST等の命令を発行し、AcceptSocket() を実行してブロック状態に入った後でも、ず〜っとブロックされてしまう状態となってしまいます。
(キューに入っていない?)

実際の命令発行は以下の通りになっています。
Dim lbytCommandBytes As Byte()
lbytCommandBytes = ASCII.GetBytes("LIST /TEST")
mclsCtrlConSocket.Send(lbytCommandBytes, lbytCommandBytes.Length, 0)
(※mclsCtrlConSocketは制御用のコネクション)

このソケットのReceive結果を取ってみると
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
のどちらかである為に、命令は受け付けてもらっていると思われます。

環境面も含め、もうちょっと調べたいと思っております。


。。。顧客先で使用している負荷分散装置がPASVモードを受け付けない仕様になっているなんて・・・しんどぃ _| ̄|○

かえで
常連さん
会議室デビュー日: 2004/09/16
投稿数: 38
投稿日時: 2005-07-12 15:18
ネット上でなんとかサンプルを見つけました。
http://members.jcom.home.ne.jp/1213687801/cs/general/ftp1.html
(※Activeモードで、LIST命令を実行。C#で作成されています)

これを連続で100回実行した所、正常に動作されました。
そこで、これをVB.NET用に自分で置き換えた所、連続100回実行の途中で、AcceptSocket() 部にてまたブロックされてしまいました。
こうなるとコーディングの問題かな?と思われます。

ちなみにAcceptSocket() を発行するまでの処理をVB.NETで置き換えたものは以下の通りです。

Dim HostName As String = Dns.GetHostName()
Dim ThisHost As IPHostEntry = Dns.GetHostByName(HostName)

Dim listener As TcpListener = New TcpListener(ThisHost.AddressList(0), 0)
Dim datasocket As Socket = Nothing
Dim retcode As Integer = 0
Dim Output As String = ""

Try
  listener.Start()

  ''PORT command
  Dim ep As IPEndPoint = CType(listener.LocalEndpoint, IPEndPoint)
  retcode = ExecCmd("PORT " & PortParam(ep.Port) & ControlChars.CrLf, Output)
  If (retcode = 0) Then Return retcode

  ''引数で指定された command
  retcode = ExecCmd(cmd, Output)
  If (retcode = 0) Then Return retcode

  ''データ受信
  Dim recvData(1024) As Byte
  Dim sb As StringBuilder = New StringBuilder
  Dim recvSize As Integer

  Console.WriteLine("接続要求があるか?:" + listener.Pending().ToString())

  datasocket = listener.AcceptSocket()

【主な修正内容】
@C#でExecCmd("PORT " & PortParam(ep.Port) & "\\r\\n", Output)
 →ExecCmd("PORT " & PortParam(ep.Port) & ControlChars.CrLf, Output)
AC#でbyte[] recvData=new byte[1024];
 →Dim recvData(1024) As Byte
BC#で
 int i1=port/256;
 int i2=port%256;
 →Dim i1 As Integer = port / 256
  Dim i2 As Integer = port Mod 256


ソースコードばかり載せてしまい、申し訳ありません。


[ メッセージ編集済み 編集者: かえで 編集日時 2005-07-12 17:01 ]
かえで
常連さん
会議室デビュー日: 2004/09/16
投稿数: 38
投稿日時: 2005-07-12 18:04
自己レスです。
解決しました。

クライアント側のポート算出が間違っていたのが原因でした。
× Dim i1 As Integer = CInt(port / 256)
○ Dim i1 As Integer = port ¥ 256

この為にPORTコマンドで送り出したポートと実際のクライアントでの受け口がずれてしまってたようです。

皆様方には大変お手数をおかけしました。
1

スキルアップ/キャリアアップ(JOB@IT)