zoukankan      html  css  js  c++  java
  • 非阻塞tcp服务器与阻塞的tcp服务器对比

    一般的tcp服务器(阻塞)是使用的如下

    [erlang] gen_tcp传输文件原型

    http://www.cnblogs.com/bluefrog/archive/2012/09/10/2679040.html

    server端

    gen_tcp:listen(开始监听端口), 如

    server(Port) ->
      case gen_tcp:listen(Port,[binary,{packet,0},
      {reuseaddr,true}, %% 
      {active,true}]) of
        {ok,Listen} ->
          spawn(fun()-> connect(Listen) end);
        {error,Why} ->
          io:format("~p~n",[Why])
      end.

    
    

    然后gen_tcp:accept(等待客户端链接,如果成功就spawn一个进程继续等待),如

    connect(Listen) ->
        case gen_tcp:accept(Listen) of %% 等待暂停并等待一个连接
            {ok,Socket} ->
                spawn(fun() -> connect(Listen) end),
                loop(Socket);
            {error,Why} ->
                io:format("~p~n",[Why])
        end.

    这里的gen_tcp:accept是阻塞的,就是说,如果没有client连接(也没有超时)就会一直等

    这是erlang推荐使用方法,可是不是最高效的方法

    还有一种非阻塞的调用一般如下

    http://www.cnblogs.com/bicowang/p/3976129.html

    使用 erlang OTP 模式编写非阻塞的 tcp 服务器

    还是使用gen_tcp:listen(开始监听端口), 如

    init([Port, Module]) ->
        process_flag(trap_exit, true),
        Opts = [binary, {packet, 2}, {reuseaddr, true},
                {keepalive, true}, {backlog, 30}, {active, false}],
        %% 使用 gen_tcp 模块启动套接字监听,这是一个阻塞动作
        case gen_tcp:listen(Port, Opts) of
        {ok, Listen_socket} -> %% 创建监听成功返回监听socket
            %% 创建第一个接受连接的进程
            %% prim_inet:async_accept/2开启异步监听
            %% 之后有客户端连接时会向此进程发送一个异步消息inet_async到进程消息队列
            %% Ref 存储接受进程的引用
            {ok, Ref} = prim_inet:async_accept(Listen_socket, -1),
            {ok, #state{listener = Listen_socket,
                        acceptor = Ref,
                        module   = Module}};
        {error, Reason} ->
            {stop, Reason}
        end.

    上面接受客户端连接没有用gen_tcp:accept,使用了prim_inet:async_accept

    当有客户端连接,会调用下面的函数

    %% 注意这里 ListSock 以及 Ref 做了匹配,只有匹配了才是该监听口接收的连接
    handle_info({inet_async, ListSock, Ref, {ok, CliSocket}},
                #state{listener=ListSock, acceptor=Ref, module=Module} = State) ->
        try
            case set_sockopt(ListSock, CliSocket) of
            ok              -> ok;
            {error, Reason} -> exit({set_sockopt, Reason})
            end,
     
            %% 接收新的客户端连接,启动一个新的客户端状态机进程,动态添加到 tcp_client_sup 客户端监控树
            {ok, Pid} = tcp_server_app:start_client(),
    
            %% 绑定 CliSocet 到客户端进程 Pid, 这样CliSocket接收数据都会被转化成Pid代表进程的邮箱消息
            gen_tcp:controlling_process(CliSocket, Pid),
            %% Instruct the new FSM that it owns the socket.
    
            Module:set_socket(Pid, CliSocket),
     
            %% Signal the network driver that we are ready to accept another connection
            %% 重新设置异步监听下一个客户端连接的消息,设置新的监听引用
            %% 必须重新设置才能监听到 {inet_async,S,Ref,Status} 消息
            case prim_inet:async_accept(ListSock, -1) of
            {ok,    NewRef} -> ok;
            {error, NewRef} -> exit({async_accept, inet:format_error(NewRef)})
            end,
             
             %% 更新新的监听引用
            {noreply, State#state{acceptor=NewRef}}
        catch exit:Why ->
            error_logger:error_msg("Error in async accept: ~p.
    ", [Why]),
            {stop, Why, State}
        end;

    这里的Ref和上面的相互对应(也和client连接使用的CliSocket对应),就是当有一个client连接,就会有inet_async消息命中

    后面使用对应的进程进行处理。

  • 相关阅读:
    java多线程
    异常处理
    mabits
    梦想改造家之全世界最治愈的家浅析
    Activity
    java基础终稿
    Visual Studio Codes配置vs2017编译
    2017-2018-2 20179216 《网络攻防与实践》SM234算法
    2017-2018-2 20179216 《网络攻防与实践》 免杀技术
    2017-2018-2 20179216 《网络攻防与实践》 SQL注入攻击
  • 原文地址:https://www.cnblogs.com/tudou008/p/7651527.html
Copyright © 2011-2022 走看看