|
学习 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 <2orelse notstring: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 >0andalso 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 after200->
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 after30000->
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>: ~wBytes~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:~wok.", [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 file125 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 ifNum ==550->
151 server_message("~w", Text);
152 true->
153 ok
154 end
155 end,
156
157 gen_tcp:close(Sock). |
|