现在的位置: 首页 > 综合 > 正文

第一个 Erlang 程序: Ftp 下载客户端收藏

2013年03月15日 ⁄ 综合 ⁄ 共 5767字 ⁄ 字号 评论关闭
  第一个 Erlang 程序: Ftp 下载客户端收藏


新一篇: 简单的语法高亮 Erlang 编辑器 | 旧一篇: pon 认证错误的一个可能的原因


  学习 Erlang 一周的习作:

   1 -module(ftp_down).
   2 -export([get/6,test/0,get_data/4]).
   3 
   4 test() ->
   5     get("ftp.ftpplanet.com", 21, "anonymous", "anonymous", "images/image001_42.jpg", "d:/").
   6 
   7 client_message(Format, Data) ->
   8     io:format("C: " ++ Format ++ "~n", Data).
   9 
  10 server_message(Format, Data) ->
  11     io:format("S: " ++ Format ++ "~n", Data).
  12 
  13 server_verify_message(Data, ExpectedCode) ->
  14     {Code, Message} = Data,
  15     ExpectedCode = Code,
  16     io:format("S: ~s~n", [Message]),
  17     ok.
  18 
  19 recv_until_EOL(Sock, StrSoFar) ->
  20     Len = string:len(StrSoFar),
  21     NotALine = Len < 2 orelse not string:equal("/r/n", string:substr(StrSoFar, Len - 1, 2)),
  22     if
  23         NotALine ->
  24             {ok, Msg} = gen_tcp:recv(Sock, 1),
  25             recv_until_EOL(Sock, StrSoFar ++ Msg);
  26         true ->
  27             StrSoFar
  28     end.
  29     
  30 recv_until_EOL(Sock) ->
  31     recv_until_EOL(Sock, "").
  32 
  33 recv_until_xxxend(Sock, Num, Line, LinesSoFar) ->
  34     ExpectedTail = integer_to_list(Num) ++ " end",
  35     ExpectedTailLen = string:len(ExpectedTail),
  36     Pos = string:len(Line) - ExpectedTailLen - 1,
  37     Eq = Pos > 0 andalso string:equal(ExpectedTail, string:to_lower(string:substr(Line, Pos, ExpectedTailLen))),
  38     if
  39         Eq ->
  40             {Num, LinesSoFar ++ Line};
  41         true ->
  42             recv_response(Sock, Num, LinesSoFar ++ Line)
  43     end.
  44 
  45 recv_response(Sock, LinesSoFar) ->
  46     Line = recv_until_EOL(Sock),
  47     {Num, [H | _]} = string:to_integer(Line),
  48     if
  49         H == $- ->
  50             recv_until_xxxend(Sock, Num, Line, LinesSoFar);
  51         true ->
  52             {Num, LinesSoFar ++ Line}
  53     end.
  54     
  55 recv_response(Sock, ResponseNo, LinesSoFar) ->
  56     Line = recv_until_EOL(Sock),
  57     recv_until_xxxend(Sock, ResponseNo, Line, LinesSoFar).
  58     
  59 recv_response(Sock) ->
  60     recv_response(Sock, []).
  61 
  62 send_command(Sock, Msg) -> 
  63    ok = gen_tcp:send(Sock, Msg ++ "/r/n"),
  64    client_message("~s", [Msg]).
  65    
  66 parse_pasv(Msg) ->
  67     Index1 = string:chr(Msg, $(),
  68     Index2 = string:chr(Msg, $)),
  69     [A1, A2, A3, A4, B1, B2] = lists:map(fun(S) -> string:strip(S) end, string:tokens(string:substr(Msg, Index1 + 1, Index2 - Index1 -1), ",")),
  70     {Nb1, _} = string:to_integer(B1),
  71     {Nb2, _} = string:to_integer(B2),
  72     <<Port:16>> = <<Nb1:8, Nb2:8>>,
  73     {lists:append([A1, ".", A2, ".", A3, ".", A4]), Port}.
  74  
  75 
  76 recv_until_transfer_complete(Sock, {ok , Msg}, MsgSoFar) ->
  77     if
  78         length(Msg) > 0 ->
  79             recv_until_transfer_complete(Sock, gen_tcp:recv(Sock, 0), MsgSoFar ++ Msg);
  80         true ->
  81             receive
  82                 transfer_complete ->
  83                     MsgSoFar
  84                 after 200 ->
  85                     recv_until_transfer_complete(Sock, gen_tcp:recv(Sock, 0), MsgSoFar ++ Msg)
  86             end
  87     end;
  88     
  89 
  90 recv_until_transfer_complete(_, {error , closed}, MsgSoFar) ->
  91     MsgSoFar.
  92 
  93 recv_until_transfer_complete(Sock) ->
  94     receive
  95         start_transfer ->
  96             recv_until_transfer_complete(Sock, gen_tcp:recv(Sock, 0), [])
  97         after 30000 ->
  98             "150 timeout"
  99     end.
 100 
 101 get_data(Addr, Port, LocalFilePath, Control) ->
 102     {ok, DataSock} = gen_tcp:connect(Addr, Port, [{active, false}]),
 103     Data = recv_until_transfer_complete(DataSock),
 104     client_message("<I GOT>: ~w Bytes~n", [length(Data)]),
 105     file:write_file(LocalFilePath, list_to_binary(Data)),
 106     gen_tcp:close(DataSock),
 107     Control ! bye.
 108     
 109     
 110 get(Host, Port, User, Pass, FilePath, LocalPath) ->
 111     Slash = string:rchr(FilePath, $/),
 112     Path = string:left(FilePath, Slash - 1),
 113     File = string:right(FilePath, string:len(FilePath) - Slash),
 114     
 115     {ok, Sock} = gen_tcp:connect(Host, Port, [{active, false}]),
 116     client_message("connect to ~s:~w ok.", [Host, Port]),
 117     server_verify_message(recv_response(Sock), 220),
 118     send_command(Sock, "USER " ++ User),
 119     server_verify_message(recv_response(Sock), 331),
 120     send_command(Sock, "PASS " ++ Pass),
 121     server_verify_message(recv_response(Sock), 230),
 122     send_command(Sock, "CWD " ++ Path),
 123     server_verify_message(recv_response(Sock), 250),
 124     send_command(Sock, "TYPE I"),
 125     server_verify_message(recv_response(Sock), 200),
 126     send_command(Sock, "PASV"),
 127     {_, Msg} = recv_response(Sock),
 128     server_message(Msg, ""),
 129     {Addr, NewPort} = parse_pasv(Msg),
 130     
 131     DataPid = spawn(ftp_down, get_data, [Addr, NewPort, LocalPath ++ File, self()]),
 132     
 133     send_command(Sock, "RETR " ++ File),
 134     % 150 Opening BINARY mode data connection for   150 File status okay; about to open data connection.
 135     % 125 Downloading in BINARY file  125 Data connection already open; transfer starting.
 136     % 550 Failed to open file
 137     {Num, Text} = recv_response(Sock),
 138     V = (Num == 150) or (Num == 125),
 139     if
 140         V ->
 141             server_message("~s", [Text]),
 142             DataPid ! start_transfer,
 143             server_verify_message(recv_response(Sock), 226),
 144             DataPid ! transfer_complete,
 145             receive
 146                 bye ->
 147                     ok
 148             end;
 149         true ->
 150             if Num == 550 ->
 151                 server_message("~w", Text);
 152             true ->
 153                 ok
 154             end
 155     end,
 156     
 157     gen_tcp:close(Sock).
 

发表于 @ 2008年06月20日 08:33:00|评论(0)|编辑|收藏


新一篇: 简单的语法高亮 Erlang 编辑器 | 旧一篇: pon 认证错误的一个可能的原因

【上篇】
【下篇】

抱歉!评论已关闭.