mochiweb 源码阅读(八)

  看来昨天的大雨给北京确实带来了重创,早上出门去海淀驾校,北清路辛庄桥路段直接就堵死了,结果好不容易慢慢走到红绿灯那,才发现前方正抽水,封路了。唉,晚上回到家,上微博发现此次因灾遇难者37人,愿逝者安息!什么时候政府才能拿我们纳税人的钱办点正事,别整天搞形象工程了,来北京那么多年了,每次大雨都没能引起重视,也别搞什么引咎辞职了,直接抓了判刑吧。唉,牢骚发的有点多了,还是继续我们的主题吧,继续跟大家分享mochiweb源码。

  在上一篇最后,我们提到mochiweb_http:start/1函数,其中就一行代码,如下:

start(Options) ->
    mochiweb_socket_server:start(parse_options(Options)).

  其中mochiweb_http:parse_options/1 函数我们在上一篇,已经详细分析了,这一篇,我们来看下函数 mochiweb_socket_server:start/1 代码如下:

start(Options) ->
    case lists:keytake(link, 1, Options) of
        {value, {_Key, false}, Options1} ->
            start_server(start, parse_options(Options1));
        _ ->
            %% TODO: https://github.com/mochi/mochiweb/issues/58
            %%   [X] Phase 1: Add new APIs (Sep 2011)
            %%   [_] Phase 2: Add deprecation warning
            %%   [_] Phase 3: Change default to {link, false} and ignore link
            %%   [_] Phase 4: Add deprecation warning for {link, _} option
            %%   [_] Phase 5: Remove support for {link, _} option
            start_link(Options)
    end.

  这里:

  < Options = [{loop,{mochiweb_http,loop,
                                  [#Fun<mochiweb_example_web.0.8815963>]}},
             {name,mochiweb_example_web},
             {ip,{0,0,0,0}},
             {port,8080}]

  首先,我们看下系统函数:lists:keytake/3,erlang doc地址:http://www.erlang.org/doc/man/lists.html#keytake-3,截图如下:

  中文注释版本,参考:erlang lists 系列函数功能与用法详解(共68个函数),截图如下:

   知道这个函数的作用,我们就能从上下文知道,如果Options列表中含有配置{link, false},则这里调用第一个分支:start_server(start, parse_options(Options1)),Options1为移除{link, false}选项后的Options列表,否则调用第二个分支:start_link(Options),函数代码如下:

start_link(Options) ->
    start_server(start_link, parse_options(Options)).

  现在我们来做个对比:

  如果Options列表中含有配置{link, false},则调用:start_server(start, parse_options(Options1));

  如果Options列表中含有配置{link, true},或者不含有该配置(这里我们可以理解为默认配置),则调用:start_server(start_link, parse_options(Options));

  那么我们看下mochiweb_socket_server:start_server/2函数的具体实现:

start_server(F, State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
    ok = prep_ssl(Ssl),
    case Name of
        undefined ->
            gen_server:F(?MODULE, State, []);
        _ ->
            gen_server:F(Name, ?MODULE, State, [])
    end.

  这里F的值为start或start_link,现在大家对Options列表中是否含有link,且值是否为true是否有进一步的理解。其实这里很简单,该选项决定了以什么方式调用gen_server,如果你现在还对gen_server一无所知,建议你移步这里Gen_Server行为,希望你看完这个能对gen_server有所了解,这里也同时推荐《Erlang/OTP并发编程实战》这本书,目前我还没有看完,刚看到第二章,收获还是不少的。

  好了,有个函数差点漏了,我们看下 mochiweb_socket_server:parse_options/1 这个函数,完整代码如下:

parse_options(State=#mochiweb_socket_server{}) ->
    State;
parse_options(Options) ->
    parse_options(Options, #mochiweb_socket_server{}).

  这里,如果配置为记录mochiweb_socket_server,则调用第一个分支,返回该配置。否则调用第二个分支,调用函数mochiweb_socket_server:parse_options/2传递Options为第一个参数,#mochiweb_socket_server{}记录为第二个参数,这个记录定义如下:

-record(mochiweb_socket_server,
        {port,
         loop,
         name=undefined,
         %% NOTE: This is currently ignored.
         max=2048,
         ip=any,
         listen=null,
         nodelay=false,
         backlog=128,
         active_sockets=0,
         acceptor_pool_size=16,
         ssl=false,
         ssl_opts=[{ssl_imp, new}],
         acceptor_pool=sets:new(),
         profile_fun=undefined}).

  函数:mochiweb_socket_server:parse_options/2 完整代码如下:

parse_options([], State) ->
    State;
parse_options([{name, L} | Rest], State) when is_list(L) ->
    Name = {local, list_to_atom(L)},
    parse_options(Rest, State#mochiweb_socket_server{name=Name});
parse_options([{name, A} | Rest], State) when A =:= undefined ->
    parse_options(Rest, State#mochiweb_socket_server{name=A});
parse_options([{name, A} | Rest], State) when is_atom(A) ->
    Name = {local, A},
    parse_options(Rest, State#mochiweb_socket_server{name=Name});
parse_options([{name, Name} | Rest], State) ->
    parse_options(Rest, State#mochiweb_socket_server{name=Name});
parse_options([{port, L} | Rest], State) when is_list(L) ->
    Port = list_to_integer(L),
    parse_options(Rest, State#mochiweb_socket_server{port=Port});
parse_options([{port, Port} | Rest], State) ->
    parse_options(Rest, State#mochiweb_socket_server{port=Port});
parse_options([{ip, Ip} | Rest], State) ->
    ParsedIp = case Ip of
                   any ->
                       any;
                   Ip when is_tuple(Ip) ->
                       Ip;
                   Ip when is_list(Ip) ->
                       {ok, IpTuple} = inet_parse:address(Ip),
                       IpTuple
               end,
    parse_options(Rest, State#mochiweb_socket_server{ip=ParsedIp});
parse_options([{loop, Loop} | Rest], State) ->
    parse_options(Rest, State#mochiweb_socket_server{loop=Loop});
parse_options([{backlog, Backlog} | Rest], State) ->
    parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog});
parse_options([{nodelay, NoDelay} | Rest], State) ->
    parse_options(Rest, State#mochiweb_socket_server{nodelay=NoDelay});
parse_options([{acceptor_pool_size, Max} | Rest], State) ->
    MaxInt = ensure_int(Max),
    parse_options(Rest,
                  State#mochiweb_socket_server{acceptor_pool_size=MaxInt});
parse_options([{max, Max} | Rest], State) ->
    error_logger:info_report([{warning, "TODO: max is currently unsupported"},
                              {max, Max}]),
    MaxInt = ensure_int(Max),
    parse_options(Rest, State#mochiweb_socket_server{max=MaxInt});
parse_options([{ssl, Ssl} | Rest], State) when is_boolean(Ssl) ->
    parse_options(Rest, State#mochiweb_socket_server{ssl=Ssl});
parse_options([{ssl_opts, SslOpts} | Rest], State) when is_list(SslOpts) ->
    SslOpts1 = [{ssl_imp, new} | proplists:delete(ssl_imp, SslOpts)],
    parse_options(Rest, State#mochiweb_socket_server{ssl_opts=SslOpts1});
parse_options([{profile_fun, ProfileFun} | Rest], State) when is_function(ProfileFun) ->
    parse_options(Rest, State#mochiweb_socket_server{profile_fun=ProfileFun}).

  从具体的函数逻辑来看,其实这个函数也很简单,就是分别去匹配Options中的每个配置项,然后按照一定格式去存储到记录#mochiweb_socket_server{}中,详细的逻辑我就不一行行跟大家分析了。如果这里你遇到什么地方不理解,欢迎大家留言跟我交流。

  我们回到mochiweb_socket_server:start_server/2函数详细分析下:

start_server(F, State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
    ok = prep_ssl(Ssl),
    case Name of
        undefined ->
            gen_server:F(?MODULE, State, []);
        _ ->
            gen_server:F(Name, ?MODULE, State, [])
    end.

  第一行,调用函数:mochiweb_socket_server:prep_ssl/1,完整代码如下:

prep_ssl(true) ->
    ok = mochiweb:ensure_started(crypto),
    ok = mochiweb:ensure_started(public_key),
    ok = mochiweb:ensure_started(ssl);
prep_ssl(false) ->
    ok.

  这里仅仅是判断配置项中是否使用SSL协议,关于这个协议,参考维基百科:安全套接层Secure Sockets LayerSSL,如果使用ssl,则对应启动这三个应用。

  第二行,判断配置项中是否存在name定义,如果存则,则使用该名字作为gen_server的名称,否则不使用名称注册该gen_server。

  当启动这个gen_server,最后会调用:mochiweb_socket_server:init/1函数,完整代码如下:

init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=NoDelay}) ->
    process_flag(trap_exit, true),
    BaseOpts = [binary,
                {reuseaddr, true},
                {packet, 0},
                {backlog, Backlog},
                {recbuf, ?RECBUF_SIZE},
                {active, false},
                {nodelay, NoDelay}],
    Opts = case Ip of
        any ->
            case ipv6_supported() of % IPv4, and IPv6 if supported
                true -> [inet, inet6 | BaseOpts];
                _ -> BaseOpts
            end;
        {_, _, _, _} -> % IPv4
            [inet, {ip, Ip} | BaseOpts];
        {_, _, _, _, _, _, _, _} -> % IPv6
            [inet6, {ip, Ip} | BaseOpts]
    end,
    listen(Port, Opts, State).

  好了,这一篇就到这里,谢谢大家的耐心阅读,下一篇,我们就从这个函数继续和大家分享mochiweb源码。

  明天还要继续练车,大家早点休息,好梦。

转载于:https://www.cnblogs.com/yourihua/archive/2012/07/23/2604264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值