FTPサーバに接続 アクティブモードでアップロード
VB2010で、FTPサーバにバイナリファイルをアクティブで送信するサンプル。
パッシブ(passive)のサンプルは、「Visual Basic .NET または Visual Basic 2005 を使用して FTP サイトにアクセスする方法」
にあるが、意外にActiveのサンプルは完成品が見つかりませんでした。
というわけで、自PCにIIS+FTPを立てて、ファイヤーウォールを切って、ゴリゴリと作ってみました。
なぜ、FTP+アクティブのサンプルが少ないか
FTPの基本は、コマンド送信ソケットと、データ送信ソケットの二つのコネクションが必要。
メール送信は一本でOKなのにね。2005 ソケットでSMTPメール送信。基本を参照してください。
アクティブとパッシブの違いは、そのデータ送信の箇所。それは、
- FTP+パッシブ=FTPツールを使うユーザは、クライアントで、サーバには接続しに行くだけ。
- FTP+アクティブ=FTPツールを使うユーザは、クライアントなのだが、サーバからの接続を待つ必要があり、擬似サーバになる必要がある。
の赤字の箇所。
要はクライアントは当然ファイヤウォール内にいることが多いわけで、外部からのアクセスは大体厳しく制限されている。
なのに、FTPの穴を開けてしまうとせっかくのファイヤーウォールが・・・・ということで、アクティブが使えないユーザが多い事が予想される。
なので、「ま、パッシブでいいんじゃない?」というのが理由らしい。
実際、FTPサーバ側は両方平行運用大丈夫なところがほとんどなので。Microsoft FTP Serviceも両方平行運用可能。
AcceptSocketで止まる(固まる・フリーズ)事があるようだが(実際管理人も?で止まったが)、
クライアント側でリッスン待ちにしたエンドポイント(IPアドレス+ポート)と、「PORT」するIPアドレスを、同じIPアドレスにしない為ではないかと思う。
少なくとも、管理人環境ではそうでした。
Imports System
Imports System.Net
Imports System.IO
Imports System.Text
Imports System.Net.Sockets
Public Class Form1
Dim sock As Socket 'コマンド用ソケット
Private ASCII As Encoding = Encoding.ASCII
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim ftps As IPEndPoint '送り先のエンドポイント(IPアドレス+ポート)
Dim Myip As IPAddress '送信元のIPアドレス
Dim Tcpl As TcpListener '送信元でFTPサーバからのアクセスを待つオブジェクト
Dim Myport As Integer '送信元のポート
sock = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
ftps = New IPEndPoint(IPAddress.Parse("192.168.11.27"), 21)
sock.Connect(ftps) 'リモート ホストへの接続を確立します。
SendCommand("USER anonymous")'ユーザ送信
SendCommand("PASS info")'パスワード送信
SendCommand("TYPE I")'バイナリモードに変換
Myip = IPAddress.Parse("192.168.11.27")
Tcpl = New TcpListener(Myip, 0) '指定したローカル IP アドレスとポート番号で受信接続の試行を待機する。0を指定して置けば任意の空いているポートを使ってくれる。
Tcpl.Start()
Dim separator As String() = Tcpl.LocalEndpoint.ToString().Split(":")
Debug.Print(separator(1).ToString)'待っている事になったポート番号を取得
Myport = CInt(separator(1))
'このIPアドレス、このポートに接続してきてねとFTPサーバにコマンド送信
SendCommand("PORT 192,168,11,27," & (Myport \ 256) & "," & Myport Mod 256)
'これから/test3.jpg送るで、とコマンド送信
SendCommand("STOR /test3.jpg")
Dim Dsocket As Socket = Tcpl.AcceptSocket
'アップロードするファイルを開く
Dim fs As New System.IO.FileStream("C:\Users\admin\Documents\Visual Studio 2010\Projects\FTP\FTP\stest.jpg", System.IO.FileMode.Open, System.IO.FileAccess.Read)
Dim buffer(fs.Length) As Byte
Dim x As Integer = CInt(fs.Length)
Dim readSize As Integer = fs.Read(buffer, 0, x)
Dsocket.SendTo(buffer, Tcpl.LocalEndpoint)
Dsocket.Close()
'//終わりましたよとコマンド送信
SendCommand("QUIT")
End Sub
'http://support.microsoft.com/kb/832679/jaから引用
' This is a function that is used to send a command to the FTP server that you are connected to.
Private Sub SendCommand(ByVal sCommand As String)
Dim bytes(255) As Byte
Dim i As Integer
sCommand = sCommand & ControlChars.CrLf
Dim cmdbytes As Byte() = ASCII.GetBytes(sCommand)
sock.Send(cmdbytes, cmdbytes.Length, 0)
' Get reply from the server.
i = sock.Receive(bytes)
Debug.WriteLine(Encoding.UTF8.GetString(bytes))
End Sub
End Class
|
最近はPHPの仕事が多いですね。
FTPでソケットを使用し、しかもアクティブで送信というサンプルを追記。
----------------------------------------------------------------------
Hello
220 Microsoft FTP Service
331 Anonymous access allowed, send identity (e-mail name) as password.
230 User logged in.
192.168.11.200:35582
200 Type set to I.
200 PORT command successful.
125 Data connection already open; Transfer starting.
42611
226 Transfer complete.
----------------------------------------------------------------------
print "Hello<br>";
//FTPアクティブでのファイル送信サンプル
//相手サーバ
$ftp_server='192.168.11.27';
$ftp_user_name='anonymous';
$ftp_user_pass='info';
//我がアパッチサーバIP
$MyserverIP="192.168.11.200";
//相手サーバ
$sock = fsockopen($ftp_server, 21);//ソケットの作成
$st=fgets($sock, 512);
print $st."<br>";
//ユーザ名
fputs($sock, "USER ".$ftp_user_name."\r\n");
$st=fgets($sock, 512);
print $st."<br>";
//パスワード
fputs($sock, "PASS ".$ftp_user_pass."\r\n");
$st=fgets($sock, 512);
print $st."<br>";
FTPBinarySend("stest.jpg","rtest.jpg");
//ソケットでFTP 送信
function FTPBinarySend($remotefile,$localfile){
global $sock;
global $MyserverIP;
// 新しいソケットを作成する データ送受信用
//FTPは、コマンド用のソケットと、データ送受信用のソケット二つを区別して使う
$dcsock= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($dcsock,SOL_SOCKET, SO_REUSEADDR, 1);
//ローカルサーバのIPに、待ちうけ用ソケットをバインド [0]を指定すると空いているポートになる
socket_bind($dcsock,$MyserverIP,0);
//FTPサーバが接続してくるのを待つ
socket_listen($dcsock);
//ポート番号を取得する
socket_getsockname($dcsock, $IP, $PORT);
print $IP.":".$PORT."\n<br>";
//print intval($PORT/256);
//exit;
fputs($sock, "TYPE I\r\n");//バイナリモードに変換
$st=fgets($sock, 512);
print $st."<br>";
//このIPアドレス、このポートに接続してきてねとFTPサーバにコマンド送信
fputs($sock, "PORT 192,168,11,200,".intval($PORT/256).",".($PORT%256)."\r\n");
$st=fgets($sock, 512);
print $st."<br>";
//これから$remotefile送るでとコマンド送信
fputs($sock, "STOR /".$remotefile."\r\n");
$st=fgets($sock, 512);
print $st."<br>";
//FTPサーバがアクセスしてきたソケットを受け付ける
$newsock = socket_accept($dcsock);
//ファイルをバイナリモードで開けてストリームに読み込む
$fp = fopen($localfile, "rb");
$contents = '';
$contents = stream_get_contents($fp);
/*
while (!feof($fp)) {
$contents .= fread($fp, 8192);
}*/
fclose($fp);
//ソケットに書き込む
$st = socket_write($newsock, $contents);
$contents = '';
print $st."<br>";
//データ送受信用は切断
socket_close($dcsock);
socket_close($newsock);
}
//終わりましたよとコマンド送信
fputs($sock, "QUIT\r\n");
$st=fgets($sock, 512);
print $st."<br>";
socket_close($sock);
exit;
|
|