最近看了几天erlang的基础语法之后还是不太明白如何运用erlang来开发,所以选择先看几个开源代码加深理解。
今天是第一天,阅读 github.com上的erlang_websocket。 Erlang_WebSocket_Server(erlang_websocket )是由davebryson和ENDOH 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) -> [].