关于简单的http

做简单网页服务器的四部曲

建立一个web访问的基本步骤,(在public中相应的代码)
(gen_tcp其实这个不是在标准库中,而是在kernel中)

1、由于http访问的本质其实也是一个socket,所以首先服务器要监听port

case gen_tcp:listen(Port, [binary,{packet, 0},{active, false}]) of
    {ok, LSock} -> server_loop(LSock,DocRoot);
    {error, Reason}    -> exit({Port,Reason})
end.

2、数据来临时,即有网页访问此地址和端口,要新建线程去应答(处理)访问
(其实一个线程其实更像是一个函数,而不是java中的类似像一个类)下面的process 就是这样子的一个线程,不断地循环,产生新的应答线程,当应答完之后自动关闭

%% main server loop - wait for next connection, spawn child to process it
server_loop(LSock,DocRoot) ->
  case gen_tcp:accept(LSock) of
    {ok, Sock} ->
      spawn(?MODULE,process,[Sock,DocRoot]),
      server_loop(LSock,DocRoot);
    {error, Reason} ->
      exit({accept,Reason})
  end.

3、在处理信息之前,必须要把数据从http数据包中分离出来,下面的操作是将二进制的数据转换成list(本质是string,不过我没有查到到底split是在什么模块中的(

注:在新的版本中是在 stdlib:re 模块里边)
case gen_tcp:recv(Sock, 0) of
    {ok, Bin} -> binary_to_list(Bin);
      {error, closed} -> exit(closed);
      {error, Reason} -> exit(Reason)

下面的process的具体操作
process(Sock,DocRoot) ->
  Req = do_recv(Sock),
  {ok,[Cmd|[Name|[Vers|_]]]} = split(Req,"[ \r\n]"),
  FileName = DocRoot ++ Name,
  LogReq = Cmd ++ " " ++ Name ++ " " ++ Vers,
  Resp = case file:read_file(FileName) of
    {ok, Data} ->
      io:format("~p ~p ok~n",[LogReq,FileName]),
      Data;
    {error, Reason}    ->
      io:format("~p ~p failed ~p~n",[LogReq,FileName,Reason]),
      error_response(LogReq,file:format_error(Reason))
    end,
  do_send(Sock,Resp),
  gen_tcp:close(Sock).

file:read_file 这个是kernel中的模块,用于读取文件(其实,是用于将其文件中的内容应答请求,返回给请求端)

4、返回数据,在3中已经有了应答的数据(即 Resp ,现在开始返回
do_send(Sock,Msg) ->
  case gen_tcp:send(Sock, Msg) of
    ok -> ok;
      {error, Reason} -> exit(Reason)
  end.

完整代码

原作者的地址,敬上

http://hideto.iteye.com/category/24824?show_full=true

2008-09-01
转贴一个简单的Web服务器:
httpd.erl
%% httpd.erl - MicroHttpd
-module(httpd).
-author("ninhenry@gmail.com").

-export([start/0,start/1,start/2,process/2]).
-import(regexp,[split/2]).

-define(defPort,8888).
-define(docRoot,"public").

start() -> start(?defPort,?docRoot).
start(Port) -> start(Port,?docRoot).  
start(Port,DocRoot) ->
  case gen_tcp:listen(Port, [binary,{packet, 0},{active, false}]) of
    {ok, LSock} -> server_loop(LSock,DocRoot);
	  {error, Reason}	-> exit({Port,Reason})
  end.

%% main server loop - wait for next connection, spawn child to process it
server_loop(LSock,DocRoot) ->
  case gen_tcp:accept(LSock) of
    {ok, Sock} ->
      spawn(?MODULE,process,[Sock,DocRoot]),
      server_loop(LSock,DocRoot);
    {error, Reason} ->
      exit({accept,Reason})
  end.

%% process current connection
process(Sock,DocRoot) ->
  Req = do_recv(Sock),

  %% {ok,[Cmd|[Name|[Vers|_]]]}=split(Req,"[ \r\n]"),
  [Cmd|[Name|[Vers|_]]]=re:split(Req,"[ \r\n]",[{return,list}]),
%%erlang suggest

 FileName = DocRoot ++ Name, LogReq = Cmd ++ " " ++ Name ++ " " ++ Vers, Resp = case file:read_file(FileName) of {ok, Data} -> io:format("~p ~p ok~n",[LogReq,FileName]), Data; {error, Reason} -> io:format("~p ~p failed ~p~n",[LogReq,FileName,Reason]), error_response(LogReq,file:format_error(Reason)) end, do_send(Sock,Resp), gen_tcp:close(Sock). %% construct HTML for failure message error_response(LogReq,Reason) -> "<html><head><title>Request Failed</title></head><body>\n" ++ "<h1>Request Failed</h1>\n" ++ "Your request to " ++ LogReq ++ " failed due to: " ++ Reason ++ "\n</body></html>\n". %% send a line of text to the socket do_send(Sock,Msg) -> case gen_tcp:send(Sock, Msg) of ok -> ok; {error, Reason} -> exit(Reason) end. %% receive data from the socket do_recv(Sock) -> case gen_tcp:recv(Sock, 0) of {ok, Bin} -> binary_to_list(Bin); {error, closed} -> exit(closed); {error, Reason} -> exit(Reason) end.

运行时在httpd.erl本地建一个public目录,public目录里放一个index.html文件
然后httpd:start()启动服务器,就可以访问http://localhost:8888/index.html了

posted on 2011-05-08 21:09  songqiuming  阅读(188)  评论(0编辑  收藏  举报

导航