【问题标题】:Erlang gen_tcp closes port that gets wrong by GCDAsynchSocketErlang gen_tcp 关闭由 GCDAsynchSocket 出错的端口
【发布时间】:2014-11-29 20:38:47
【问题描述】:

我遇到了一个问题。我在 Erlang/OTP 上有一个 iOS 客户端和一个 tcp 服务器。客户端假设通过 GCDAsynchSocket 向服务器发送和接收消息。如果我需要发送消息,它工作得很好,但它不作为接受者,因为客户端必须调用这个委托方法:

/** * Called when a socket has completed reading the requested data into memory. * Not called if there is an error. **/ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;

问题是服务器关闭了连接,它调用了另一个委托方法( - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err; )。 如何在客户端完成从服务器读取数据以及如何在服务器上留下工作连接,直到客户端自行断开连接? iOS 客户端从服务器获取字节,但无法正常关闭连接。

Erlang 服务器的一部分:

-behaviour (gen_server).

-export ([start_link/0, check_data/1]).

%%gen_server callbacks
-export ([init/1, handle_call/3,
        handle_cast/2, handle_info/2,
        terminate/2, code_change/3]).

-define (PORT, 1477).

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


init([Port]) ->
    process_flag(trap_exit, true),
    {ok, Listen} = gen_tcp:listen(Port, 
        [{active, false},
         binary,
         {reuseaddr, true}]), 
    spawn(fun() ->
    accept_parallel(Listen) end),
    io:format("~p started~n", [?MODULE]),
    {ok, 0}.

accept_parallel(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    spawn(fun() -> accept_parallel(Listen)  end),
    loop(Socket).

handle_call(Request, _From, N) ->
    {reply, Request, N + 1}.

handle_cast(_Msg, N) -> 
    {noreply, N}.

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

terminate(_Reason, _N) ->
    io:format("~p stoped~n", [?MODULE]),
    ok.

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

loop(Socket) ->
    case gen_tcp:recv(Socket, 0) of
        {ok, Bin} ->
            case check_data(Bin) of
            ok ->
                gen_tcp:send(Socket, "ok");
            {error, _Data} ->
                gen_tcp:send(Socket, "error")       
            end;
        {error, Reason} ->
            exit(Reason)
    end.

我可以在向客户端发送消息后设置超时,但无论如何我怎样才能完成接受数据呢?

更新:我只需连接一次即可通过向客户端发送令牌来证明授权。

【问题讨论】:

    标签: ios objective-c tcp erlang gcdasyncsocket


    【解决方案1】:

    您的循环没有循环,因此一旦评估了您的案例表达式,您的进程就会退出(无事可做)并且 gen_tcp 会在进程退出时为您关闭连接。你需要在loop/1的末尾添加loop(Socket)

    loop(Socket) ->
        case gen_tcp:recv(Socket, 0) of
            % stuff...
        end,
        loop(Socket).
    

    不过,我个人更喜欢使用更通用的receive 样式与 gen_tcp,而不是 gen_tcp:recv/2,如下例所示:

    loop(S) ->
        inet:setopts(S,[{active,once}]),
        receive
            {tcp,S,Data} ->
                Answer = process(Data), % business logic here
                gen_tcp:send(S,Answer),
                loop(S);
            {tcp_closed,S} ->
                io:format("Socket ~w closed [~w]~n",[S,self()]),
                ok
        end.
    

    上面的示例来自gen_tcp 文档,与您将看到的人们实现混合套接字样式的方式非常相似。至少对我来说,这种方法在防止数据泛滥的同时澄清了我的推理。

    【讨论】:

    • 可能,我应该说我只需要连接一次即可通过将令牌发送回客户端来证明授权。无论如何,非常感谢
    • @AndreyNechaev 就身份验证协议的机制而言,在 TCP 中连接“仅一次”(尤其是用于授权)几乎从未真正接收/发送一次(但它可能是)。另外,请注意,像我上面所做的那样简单地添加loop(Socket) 意味着你的代码会循环直到它崩溃——你需要在那里的某个地方调用exit。无论如何,我使用更通用的 receive 而不是 gen_tcp:recv 的主要原因是因为它阻止我编写嵌套条件 - 我真的讨厌在函数式语言中这样做。
    • 你的回答作为风格推荐很好,我有几乎相同的循环方法,现在我又回到了它。感谢您的意见。我想,我的问题更多的是关于 gcdasynchsocket。无论如何,那是我的公寓,我无法写清楚。对不起
    【解决方案2】:

    我找到了解决方案。我希望它对某人有所帮助。 服务器上的一点修正:

    loop(Socket) ->
        inet:setopts(Socket, [{active, once}]),         
        receive
            {tcp, Socket, Bin} ->
                Answer = check_data(Bin),
                gen_tcp:send(Socket, atom_to_binary(Answer, utf8)),
                loop(Socket);
            {tcp_closed, Socket} ->
                io:format("Socket ~p closed [~p]~n", [Socket, self()]),
                ok  
        end.    
    
    check_data(Bin) ->
        Data = mochijson:decode(Bin),
        case Data of
                    {struct, [{"login", Name}, {"password", Password}]} = NewUser ->
                        io:format("We got a new user:~n  Name - ~p~n  Password - ~p~n", [Name, Password]),
                        spawn(fun() -> save_data(NewUser) end),
                        ok;
                    Any ->
                        io:format("We got ~p~n", [Any]),
                        error   
                end. 
    

    和客户:

    #pragma mark - managing TCP connection
    
    - (IBAction)signInPressed:(id)sender {
        NSString *login = [_loginTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
        NSString *password = [_passwordTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    
        NSDictionary *loginInformation = @{@"login" : login, @"password" : password};
    
        NSData *data = [NSJSONSerialization dataWithJSONObject:loginInformation
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:nil];
        //[socket readDataWithTimeout:-1 tag:0]; if you need to get more than one response
        [socket writeData:data withTimeout:-1 tag:1];
    }
    
    - (void)initTCPConnection
    {
        NSError *err;
    
        if (![socket connectToHost:@"192.168.1.7" onPort:1477 error:&err]) {
            NSLog(@"We got an error - %@", err);
        }
    
        NSLog(@"We are fine");
    }
    
    - (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port
    {
        NSLog(@"connected!");
        [socket readDataWithTimeout:-1 tag:0];
    }
    
    - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
    {
        NSLog(@"First request sent");
    }
    
    -(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
    
        NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"MSG: %@",msg);
    }
    

    就我而言,我无法使用 readDataToData: 等其他方法进入 didReadData。祝你好运。

    【讨论】:

    • 哇。事实证明,这一次更容易阅读。干得好。
    • 感谢您的推荐。 @zxq9
    猜你喜欢
    • 2011-08-12
    • 2012-04-02
    • 2012-09-21
    • 1970-01-01
    • 1970-01-01
    • 2014-07-10
    • 2013-05-06
    • 1970-01-01
    • 2019-06-28
    相关资源
    最近更新 更多