mochiweb源码阅读3-消息交互

本文介绍了 Mochiweb 中 Req:get(socket) 实际上是调用了 mochiweb_request/2 函数,探讨了参数化模块的概念。通过错误信息和调试器,我们可以看到 Req:get(1) 实际执行的是 request:get(1, [1,2,3])。默认参数必须是元组,如 {?MODULE, [....]},以支持不同模块实例的对象创建。" 79769848,5677862,班级博客项目实践:jsp + servlet 搭建,"['jsp', 'java', 'web开发', 'MVC框架', '项目实战']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇主要梳理的是mochiweb整体连接的处理机制,这篇主要梳理mochiweb如何和外界进行交互
接下来我们回到mochiweb_acceptor文件中init/4函数,我们已经走完了do_accept/2函数路径,接下来继续往下看,这里为了方便查看,再贴一遍mochiweb_acceptor:init/4代码
init(Server, Listen, Loop, Opts) ->
    case catch do_accept(Server, Listen) of
        {ok, Socket} ->
            call_loop(Loop, Socket, Opts);
        {error, Err} when Err =:= closed orelse
                          Err =:= esslaccept orelse
                          Err =:= timeout ->
            exit(normal);
        Other ->
            %% Mitigate out of file descriptor scenario by sleeping for a
            %% short time to slow error rate
            case Other of
                {error, emfile} ->
                    receive
                    after ?EMFILE_SLEEP_MSEC ->
                            ok
                    end;
                _ ->
                    ok
            end,
            error_logger:error_report(
              [{application, mochiweb},
               "Accept failed error",
               lists:flatten(io_lib:format("~p", [Other]))]),
            exit({error, accept_failed})
    end.
执行完do_accept/2若执行正常则执行call_loop/3,我们现在从call_loop/3开始分析
call_loop({M, F}, Socket, Opts) ->
    M:F(Socket, Opts);
call_loop({M, F, [A1]}, Socket, Opts) ->
    M:F(Socket, Opts, A1);
call_loop({M, F, A}, Socket, Opts) ->
    erlang:apply(M, F, [Socket, Opts | A]);
call_loop(Loop, Socket, Opts) ->
    Loop(Socket, Opts).
这里可以看到call_loop四次重载仅仅存在第一个参数格式的不同或者为空,可以查看源码阅读2得知Loop应为{mochiweb_http,loop,[{keepalive,loop}]}由此可知会调用call_loop第二次重载,带入参数,则会执行mochiweb_http:loop(Socket,Opts,[{keepalive,loop}],接下来查看mochiweb_http:loop/3
loop(Socket, Opts, Body) ->
    ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, http}])),
    request(Socket, Opts, Body).

request(Socket, Opts, Body) ->
    ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{active, once}])),
    receive
        {Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl ->
            ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, httph}])),
            headers(Socket, Opts, {Method, Path, Version}, [], Body, 0);
        {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->
            request(Socket, Opts, Body);
        {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl ->
            request(Socket, Opts, Body);
        {tcp_closed, _} ->
            mochiweb_socket:close(Socket),
            exit(normal);
        {ssl_closed, _} ->
            mochiweb_socket:close(Socket),
            exit(normal)
    after ?REQUEST_RECV_TIMEOUT ->
        mochiweb_socket:close(Socket),
        exit(normal)
    end.
loop/2首先调用mochiweb_socket:setopts/2针对是否使用ssl协议,采取不同的处理,修改Socket相关配置为{packet,http},设定http协议打包配置,然后调用mochiweb_http:request/3,在request/3中再次调用setopts/2修改Socket相关配置为{active,noce},{active,noce}相关介绍可查看《Erlang程序设计(第二版)》第十七章套接字编程,混合消息接收(部分阻塞式)内容request/3针对不同接收信息执行不同处理,超时或退出信息执行Socket关闭操作,http_error信息递归调用request/3,http_request信息则调用mochiweb_http:headers/6解析http头信息
headers(Socket, Opts, Request, Headers, _Body, ?MAX_HEADERS) ->
    %% Too many headers sent, bad request.
    ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
    handle_invalid_request(Socket, Opts, Request, Headers);
headers(Socket, Opts, Request, Headers, Body, HeaderCount) ->
    ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{active, once}])),
    receive
        {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl ->
            Req = new_request(Socket, Opts, Request, Headers),
            call_body(Body, Req),
            ?MODULE:after_response(Body, Req);
        {Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl ->
            headers(Socket, Opts, Request, [{Name, Value} | Headers], Body,
                    1 + HeaderCount);
        {tcp_closed, _} ->
            mochiweb_socket:close(Socket),
            exit(normal);
        {tcp_error, _, emsgsize} = Other ->
            handle_invalid_msg_request(Other, Socket, Opts, Request, Headers)
    after ?HEADERS_RECV_TIMEOUT ->
        mochiweb_socket:close(Socket),
        exit(normal)
    end.
从上可知headers分为两个分支,若HeaderCount==?MAX_HEADERS时进入第一个分支,由注释可知,头文件信息过多,判定为无效连接headers第二分支则是循环解析http协议头,提取相关Header元素,直到解析完毕(http_eoh),然后调用mochiweb_http:new_request/4创建一个request对象,并调用mochiweb_http:call_body/2,最后在调用mochiweb_http:after_response/2
这里补充一下这里获取的消息格式(HttpRequest,HttpResponse在http协议中只应用与第一行-request/3已处理)
{http,Socket,HttpPacket}
HttpPacekt=HttpHeader|http_eoh
HttpHeader={http_header,integer(),HttpField,Reserved::term(),Value::HttpString}
首先我们一起看一下mochiweb_http:new_request/4
new_request(Socket, Opts, Request, RevHeaders) ->
    ok = mochiweb_socket:exit_if_closed(mochiweb_socket:setopts(Socket, [{packet, raw}])),
    mochiweb:new_request({Socket, Opts, Request, lists:reverse(RevHeaders)}).
转入mochiweb:new_request/1
new_request({Socket, {Method, HttpUri, Version}, Headers}) ->
    new_request({Socket, [], {Method, HttpUri, Version}, Headers});

new_request({Socket, Opts, {Method, HttpUri, Version}, Headers}) ->
    mochiweb_request:new(Socket,
                         Opts,
                         Method,
                         uri(HttpUri),
                         Version,
                         mochiweb_headers:make(Headers)).

这里mochiweb_headers:make/1主要作用是存储headers中相关元素,使用到gb_trees(General balanced trees)模块,可自行查看手册
接下来继续跟踪mochiweb_request:new/6
%% @spec new(Socket, Method, RawPath, Version, headers()) -> request()
%% @doc Create a new request instance.
new(Socket, Method, RawPath, Version, Headers) ->
    new(Socket, [], Method, RawPath, Version, Headers).

%% @spec new(Socket, Opts, Method, RawPath, Version, headers()) -> request()
%% @doc Create a new request instance.
new(Socket, Opts, Method, RawPath, Version, Headers) ->
    {?MODULE, [Socket, Opts, Method, RawPath, Version, Headers]}.
从注释可知,这里new/6函数是创建一个request实例并返回
Req={mochiweb_request,[Socket,Opts,Method,RawPath,Version,Headers]}
然后回到mochiweb_http:headers/6继续执行mochiweb_http:call_body/2
call_body({M, F, A}, Req) ->
    erlang:apply(M, F, [Req | A]);
call_body({M, F}, Req) ->
    M:F(Req);
call_body(Body, Req) ->
    Body(Req).
call_body传入的第一个参数从上文可知为[{keepalive,loop}],然后直接调用keepalive:loop(Req),keepalive属于自定义模块,自定义loop可以针对不同的情况对请求进行处理响应,有兴趣的可以自行了解一下最后查看mochiweb_http:after_response/2,终于到要到最后了,我已经头晕目眩了,不知道还有没有人坚持看到这里
after_response(Body, Req) ->
    Socket = Req:get(socket),
    case Req:should_close() of
        true ->
            mochiweb_socket:close(Socket),
            exit(normal);
        false ->
            Req:cleanup(),
            erlang:garbage_collect(),
            ?MODULE:loop(Socket, mochiweb_request:get(opts, Req), Body)
    end.

Req:get(socket)实际上会调用mochiweb_request/2,这种用法称为参数化的Module(Parameterized Module),这里不做详细梳理这里可以做个测试:

通过报错信息可以得知Req:get(1)调用的是request:get/2,通过debuger可以发现实际执行的是request:get(1,[1,2,3]),此外还有一点需要注意的是,默认参数必须为元组即模块实例{?MODULE,[....]},这样针对不同模块实例可产生多个对象



基于python实现的粒子群的VRP(车辆配送路径规划)问题建模求解+源码+项目文档+算法解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值