阅读源码学习erlang:erlang_websocket (1) - websocket_server.erl

最近看了几天erlang的基础语法之后还是不太明白如何运用erlang来开发,所以选择先看几个开源代码加深理解。

今天是第一天,阅读 github.com上的erlang_websocket。  Erlang_WebSocket_Server(erlang_websocket )是由davebrysonENDOH Takanao 合作使用erlang编写的服务器侧websocket实现。

好了,开始看代码.

下面是 erlang_websocket 的核心部分 websocket_server.erl 代码注释.

  1 -module(websocket_server).
  2 -import(handshake, [handshake/1]).
  3 -export([start/0, start/5, default_echo_handler/0, unicast/2, broadcast/2, sendall/1]).
  4 %-compile(export_all).
  5 
  6 %%启动服务器 监听本地的,9000端口 MODULE 是一个宏
  7 start() -> start("localhost", 9000, ?MODULE, default_echo_handler, []).
  8 
  9 %% 通过参数启动服务器
 10 start(Host, Port, Module, Handler, Args) -> 
 11   %% 获得对应的IP地址
 12   {ok, IPaddress} = inet:getaddr(Host, inet), 
 13   %% 对这个地址和端口进行监听. [{ip, IPaddress}, {packet, 0}, {reuseaddr, true}, {keepalive, false}, {active, false}] 是tcp接口参数
 14   {ok, ListenSocket} = gen_tcp:listen(Port, [{ip, IPaddress}, {packet, 0}, {reuseaddr, true}, {keepalive, false}, {active, false}]),
 15   
 16   %% 将接收数据的循环放入到一个进程中运行,并且将这个进程注册名字为receiver 
 17   %% spawn 为erlang内建函数,用来创建一个轻量进程,函数原型是:spawn(Node, Mod, Func, Args) ??对这个函数原型不是很熟悉
 18   %% register为erlang内建函数,用来将一个名字注册到一个进程
 19 
 20   register(receiver, spawn(fun() -> receiver_loop() end)),
 21   %% 将发送数据的循环放入到一个进程中运行,并且将这个进程注册名字为sender
 22   register(sender, spawn(fun() -> sender_loop([]) end)),
 23   %% 将收到数据的处理操作放入到一个进程中运行,并且将这个进程注册名字为handler
 24   register(handler, spawn(Module, Handler, Args)),
 25   %% 执行循环:等待连接上来
 26   accept_connect_loop(ListenSocket).
 27 
 28 %%接受逻辑循环
 29 receiver_loop() ->
 30   %%收到消息如果是data,那么解析对应的Frame,并且获得SocketSenderPid对应的列表,将该消息转发给到handler进程,然后继续 receiver_loop
 31   %%收到消息如果是连接open。。。
 32   %%收到消息如果是连接close。。。
 33   %%收到消息如果是连接error。。
 34   %%其他。。。
 35   receive 
 36     {data, Frame, SocketSenderPid} -> 
 37       handler ! {message, decode_frame(Frame), integer_to_list(erlang:phash2(SocketSenderPid))},
 38       receiver_loop();
 39     {open, SocketSenderPid} -> 
 40       handler ! {open, integer_to_list(erlang:phash2(SocketSenderPid))},
 41       receiver_loop();
 42     {closed, SocketSenderPid} -> 
 43       handler ! {closed, integer_to_list(erlang:phash2(SocketSenderPid))},
 44       receiver_loop();
 45     {error, PosixReason, SocketSenderPid} -> 
 46       handler ! {error, PosixReason, integer_to_list(erlang:phash2(SocketSenderPid))},
 47       receiver_loop();
 48     _Any ->
 49       receiver_loop()
 50   end.
 51 
 52 %%消息处理
 53 default_echo_handler() ->
 54   receive
 55     %%处理 message ,之后继续循环
 56     {message, Data, ConnectionID} -> 
 57       unicast(Data, ConnectionID), %% 将这个数据进行 unicast(单向发送)
 58       default_echo_handler();
 59     _Any -> 
 60       default_echo_handler()
 61   end.
 62 
 63 %%将消息发送出去,具体发送处理见 sender_loop
 64 unicast(Data, ConnectionID) ->
 65   sender ! {unicast, Data, ConnectionID}.
 66 
 67 %%广播出去消息
 68 broadcast(Data, ConnectionID) ->
 69   sender ! {broadcast, Data, ConnectionID}.
 70 
 71 %%发送数据到所有人
 72 sendall(Data) ->
 73   sender ! {sendall, Data}.
 74 
 75 %%发送循环
 76 sender_loop(ConnectionIDPidList) ->
 77   receive
 78     %% 假如消息是一个连接打开,那么把这个消息的发送者id添加到连接列表
 79     {open, SocketSenderPid} ->
 80       sender_loop([{integer_to_list(erlang:phash2(SocketSenderPid)), SocketSenderPid}|ConnectionIDPidList]);
 81 
 82     %% 将消息发送给所指定的连接
 83     {unicast, Data, ConnectionID} ->
 84       {_, SocketSenderPid} = lists:keyfind(ConnectionID, 1, ConnectionIDPidList),
 85       SocketSenderPid ! {send, Data},
 86       sender_loop(ConnectionIDPidList);
 87 
 88     %%将消息广播到其他的连接 lists:map(fun(X) -> element(2, X) end, 这个操作不是很清楚???
 89     {broadcast, Data, ConnectionID} ->
 90       SocketSenderPidList = lists:map(fun(X) -> element(2, X) end, lists:keydelete(ConnectionID, 1, ConnectionIDPidList)),
 91       sendall(Data, SocketSenderPidList),
 92       sender_loop(ConnectionIDPidList);
 93 
 94     %%发一个消息给到所有的连接
 95     {sendall, Data} ->
 96       SocketSenderPidList = lists:map(fun(X) -> element(2, X) end, ConnectionIDPidList),
 97       sendall(Data, SocketSenderPidList),
 98       sender_loop(ConnectionIDPidList);
 99 
100     %%关闭掉一个连接:从连接列表中移除
101     {closed, SocketSenderPid} ->
102       sender_loop(lists:keydelete(SocketSenderPid, 2,ConnectionIDPidList));
103     _Any -> 
104       sender_loop(ConnectionIDPidList)
105   end.
106 
107 sendall(_Data, []) -> ok;
108 
109 %% 将消息逐个发给到列表中每个连接
110 sendall(Data, [H|T]) ->
111   H ! {send, Data},
112   sendall(Data, T).
113 
114 %% 接收连接的循环
115 accept_connect_loop(ListenSocket) ->
116   {ok, Socket} = gen_tcp:accept(ListenSocket),
117   %% 对新收到一个连接初始化
118   spawn(fun() ->  init(Socket) end),
119   %% 继续监听连接
120   accept_connect_loop(ListenSocket).
121 
122 init(Socket) ->
123   %% Set to http packet here to do handshake
124   %% 设置连接的属性
125   inet:setopts(Socket, [{packet, http}]),
126 
127   %% handshake 实现了消息接收的处理操作,主要是针对HTML5中websocket协议处理
128   ok = handshake(Socket),
129 
130   %%设置属性 ??这个 inet:setopts 不是很熟悉???具体接口原型是?
131 
132   inet:setopts(Socket, [list, {packet, raw}, {active, false}]),
133   
134   %% 分别给这个sokect分配一个发送循环(socket_sender_loop)进程
135   SocketSenderPid = spawn(fun() -> socket_sender_loop(Socket) end),
136   
137   %% 分别给这个sokect分配一个接收循环(socket_receiver_loop)进程
138   spawn(fun() -> socket_receiver_loop(Socket, SocketSenderPid) end),
139 
140   %% 通知 sender,receiver 连接已经打开
141   sender ! {open, SocketSenderPid},
142   receiver ! {open, SocketSenderPid}.
143 
144 %% 将收到的消息转发到 receiver 和 sender
145 socket_receiver_loop(Socket, SocketSenderPid) ->
146   case gen_tcp:recv(Socket, 0) of 
147     {ok, Frame} ->
148       receiver ! {data, Frame, SocketSenderPid},
149       socket_receiver_loop(Socket, SocketSenderPid);
150     {error, closed} ->
151       %% 收到连接关闭的消息,进行关闭进程
152       sender ! {closed, SocketSenderPid},
153       receiver ! {closed, SocketSenderPid},
154       SocketSenderPid ! closed,
155       gen_tcp:close(Socket);
156     {error, PosixReason} ->
157       sender ! {error, PosixReason, SocketSenderPid},
158       receiver ! {error, PosixReason, SocketSenderPid},
159       SocketSenderPid ! {error, PosixReason},
160       gen_tcp:close(Socket);
161     _Any -> 
162       exit(normal)    
163   end. 
164 
165 %%消息发送循环进程
166 socket_sender_loop(Socket) -> 
167   receive
168     {send, Data} ->
169       %% 将数据进行发送出去
170       gen_tcp:send(Socket, [0] ++ Data ++ [255]),
171       socket_sender_loop(Socket);
172     closed ->
173       %% 收到连接关闭的消息,进行关闭进程
174       exit(normal);
175     {error, PosixReason} ->
176       exit(PosixReason); 
177     _Any -> 
178       socket_sender_loop(Socket)
179   end.
180 
181 %%解析数据帧
182 decode_frame([0|T]) -> decode_frame1(T);
183 decode_frame(_Any) -> []. 
184 decode_frame1([255]) -> [];
185 decode_frame1([H|T]) -> [H|decode_frame1(T)];
186 decode_frame1(_Any) -> [].

 

 

 

posted on 2013-03-21 22:27  Ryan_Y  阅读(891)  评论(0编辑  收藏  举报