0

我正在为 Erlang 开发一个邮件客户端适配器。当我尝试执行 fetch 命令时遇到问题,Erlang 无法获取正文的内容。

这是我的终端的输出,当我尝试通过 netcat 使用该命令时:

4 FETCH 2 BODY[2]
* 2 FETCH (BODY[2] {1135}

                <div>
                    test content
                </div>
            )
4 OK FETCH completed.

gen_tcp 服务器能够接收的唯一输出是这个二进制文件:

<<"* 2 FETCH (BODY[2] {1135}\r\n">>

源代码在这里:

-module(mailconnector).

-behaviour(gen_server).

-export([start_link/2, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link(Host, imap) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [Host, 143], []).

stop() ->
    gen_server:call(?MODULE, stop).

init([Host, Port]) ->
    {ok, Sock} = gen_tcp:connect(Host, Port, [binary, {packet, 0}, {active, true}]),
    {ok, {Sock, 0}}.

handle_call(stop, _From, State) ->
    {stop, normal, ok, State};

handle_call({login, Username, Password}, _From, State) ->
    {NewState, Output} = action(State, string:join(["LOGIN", Username, Password], " ")),
    case Output of
        {ok, Response, Data} -> Result = {Response, Data};
        _ -> Result = false
    end,
    {reply, Result, NewState};

handle_call(list, _From, State) ->
    {NewState, Resp} = action(State, "LIST \"\" \"*\""),
    {reply, Resp, NewState};

handle_call({select, MailBox}, _From, State) ->
    {NewState, Output} = action(State, string:join(["SELECT", MailBox], " ")),
    case Output of
        {ok, Response, Data} -> Result = {Response, Data};
        _ -> Result = false
    end,
    {reply, Result, NewState};

handle_call({fetch, Num}, _From, State) ->
    {NewState, Output} = action(State, string:join(["FETCH", Num, "BODY[1]"], " ")),
    case Output of
        {ok, Response, Data} -> Result = {Response, Data};
        _ -> Result = false
    end,
    {reply, Result, NewState};

handle_call(_Command, _From, _State) ->
    {reply, not_valid, _State}.

handle_cast(_Command, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

action(_State = {Socket, Counter}, Command) ->
    NewCount = Counter+1,
    CounterAsList = lists:flatten(io_lib:format("~p ", [NewCount])),
    Message = list_to_binary(lists:concat([CounterAsList, Command, "\r\n"])),
    io:format("~p~n", [Message]),
    gen_tcp:send(Socket, Message),
    {{Socket, NewCount}, listener(Socket, NewCount)}.

listener(_Sock, Count) ->
    receive
    {_, _, Reply} ->
        io:format("RECEIVED: ~p~n", [Reply]),
        Messages = string:tokens(binary_to_list(Reply), "\r\n"),
        io:format("~p~n", [Messages]),
        find_message(Messages, Count)
    after 5000 ->
        timeout
    end.

process_message(Message, Count) ->
    StringCount = lists:flatten(io_lib:format("~p", [Count])),
    case [MCount|PureMessage] = string:tokens(Message, " ") of
        _M when StringCount == MCount ->
            {ok, string:join(PureMessage, " ")};
        _ -> [_Command|Output] = PureMessage, {data, string:join(Output, " ")}
    end.

find_message(Messages, Count) ->
    find_message(Messages, Count, []).

find_message([], _, _) ->
 false;    

find_message([H|T], Count, Data) ->
    case process_message(H, Count) of
        {ok, Message} -> {ok, Message, lists:reverse(Data)};
        {data, Output} -> find_message(T, Count, [Output|Data])
    end.

非常感谢你的帮助。

4

2 回答 2

1

以下只是评论,而不是我相信上面提供的问题的答案。

由于您使用的是标准行为之一(gen_server),因此您会假设您打算编写符合 OTP 的应用程序。如果是这样,除非您准备好处理所有可能的系统消息以及您的应用程序的消息,否则您永远不应该直接使用接收表达式。在 gen_server 或 gen_fsm 的情况下,非系统消息由您的 handle_info/2 回调函数处理。您可以使用 State 变量来保存您正在处理的命令(例如登录)的指示符,并为每个命令设置一个单独的子句:

 handle_info({tcp,Socket,Reply}, #state{pending = login} = State) ->
 ...;
 handle_info({tcp,Socket,Reply}, #state{pending = list} = State) ->
 ...;

...然而,这将成为一个穷人的有限状态机,所以你最好将它移植到一个 gen_fsm 行为回调模块,在那里你有一个单独的状态(即 wait_for_login)。然后你可以使用handle_info/3:

 handle_info({tcp,Socket,Reply}, wait_for_login, State) ->
 ...;
 handle_info({tcp,Socket,Reply}, wait_for_list, State) ->
 ...;
于 2013-05-13T10:36:26.047 回答
0

如果您处于active模式,最好的方法是使用模式在您的handle_info(或您的自定义接收函数)中接收流{tcp, Socket, Msg}并将其存储在缓冲区中,直到它满足特定的模式匹配或具有特定的长度,然后您根据需要刷新缓冲区。

正如@rvirding 所说,您不能确定您的所有消息都会在一个数据包中收到,因此您必须处理可能的多个数据包。否则,您必须使用passive模式和功能gen_tcp:recv/2,但请记住,此功能是阻塞的。

我建议你阅读这个: http: //learnyousomeerlang.com/buckets-of-sockets

于 2013-05-13T10:09:08.600 回答