自己写的一个简单的基于gen_tcp行为的聊天室:
因为技术要求,要服务器端要实现3个节点,分别是(node,holl,chat节点)。
客户端要实现的是 和服务端的通信,注册,登陆和聊天的功能
(1)
opt_node模块:用于监听port端口(自己定义的),并且用于监听多个客户端发来的请求。
-module(opt_node).
%% ====================================================================
%% API functions
%% ====================================================================
-behaviour(gen_server).
-export([]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-compile(export_all).
-define(Server_Node,node).
-define(Node_Holl,holl@simsunny).
-define(Server_Holl,holl).
-define(Node_Chat,chat@simsunny).
-define(Server_Chat,chat).
%% ====================================================================
%% Internal functions
%% ====================================================================
s() -> gen_server:start_link({local,?Server_Node}, ?MODULE, [], []).
stop() -> gen_server:call(?Server_Node, stop).
init([])->
io:format("-----------------supervison------------------------\n"),
server(),
{ok,[]}.
handle_call(stop, _From, Tab) ->
{stop, normal, stopped, Tab}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State,_Extra) -> {ok, State}.
server()->
io:format("----------------server------------------------\n"),
case gen_tcp:listen(2345,[binary,{packet,4},
{reuseaddr,true},{active,true}] ) of
{ok,Listen}->
spawn(fun()->connect(Listen,0)end);
{error,Reason}->
io:format(" listen is wrong:~p~n",[Reason])
end.
connect(Listen,Num)->
case gen_tcp:accept(Listen) of
{ok,Socket}->
io:format("--------------------accpet()----------------------\n"),
io:format("Socket is:~p~n", [Socket]),
Server_name=list_to_atom("opt_node_server"++integer_to_list(Num)),
%% Server_name_super=list_to_atom("super_node_server"++integer_to_list(Num)),
New_num=Num+1,
{ok,Pid}=opt_node_connect:s(Server_name),
gen_tcp:controlling_process(Socket, Pid),
connect(Listen,New_num);
{error,Why}->
io:format("par_con error:~p~n",[Why])
end.
函数解释:
gen_tcp:listen(2345,[binary,{packet,4},{reuseaddr,true},{active,true}] ) 表示监听2345的端口,
spawn(fun()->connect(Listen,0)end) 这里用spawn是应为调用 gen_tcp:listen函数,进程一直在等待连接,开一个新的进程用于处理请求
gen_tcp:controlling_process(Socket, Pid),表示将Socket交给Pid的进程来处理
(2)
opt_node_connect 模块:当客户端发来请求,就新建一个进程,该进程与客户端进行通信
-module(opt_node_connect).
%% ====================================================================
%% API functions
%% ====================================================================
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-compile(export_all).
-define(Node_Holl,holl@simsunny).
-define(Server_Holl,holl).
-define(Node_Chat,chat@simsunny).
-define(Server_Chat,chat).
%% ====================================================================
%% Internal functions
%% ====================================================================
s(Server_name) ->
gen_server:start_link({local,Server_name},?MODULE, [Server_name], []).
init([Server_name])->
{ok,Server_name}.
%% ====================================================================
%%handle_call
%% ====================================================================
%%quite
handle_call(stop, _From,Server_name) ->
{stop, normal, stopped, Server_name}.
%% ====================================================================
%%handle_cast
%% ====================================================================
%%register
handle_cast({reigister,user_is_already,{socket,Socket}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("register is error (user is already)\n"),
gen_tcp:send(Socket,term_to_binary({register,n,user_is_already})),
{noreply, Tab};
handle_cast({reigister,register_success,{{user,Name},{psw,Psw},{socket,Socket}}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("register is ok \n"),
gen_tcp:send(Socket,term_to_binary({register,y,{{user,Name},{psw,Psw}}})),
{noreply, Tab};
%%login
handle_cast({login,no_urers,{socket,Socket}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("log is error (no users)\n"),
gen_tcp:send(Socket,term_to_binary({login,n,no_urers})),
{noreply, Tab};
handle_cast({login,psw_wrong,{socket,Socket}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("log is error (psw wrong)\n"),
gen_tcp:send(Socket,term_to_binary({login,n,psw_wrong})),
{noreply, Tab};
handle_cast({login,success,{{user,Name},{psw,Psw},{socket,Socket}}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("log is ok\n"),
gen_tcp:send(Socket,term_to_binary({login,y,{{user,Name},{psw,Psw}}})),
%% ets:insert(?Ets_Tab_Users, {Name,Socket}),
{noreply, Tab};
%%get online
handle_cast({get_all_users,List_name,{socket,Socket}},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("get_all_users\n"),
gen_tcp:send(Socket,term_to_binary({get_all_users,List_name})),
{noreply, Tab};
%%chat in room
handle_cast({chat,{name,Name},{content,Msg},List},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("chat\n"),
send(Name, List, Msg),
{noreply, Tab};
%%chat in group
handle_cast({chat_gr,{name,Name},{content,Content},List_node},Tab)->
io:format("------------------receive node-------------------------------\n"),
io:format("chat_gr\n"),
send(Name, List_node, Content),
{noreply, Tab};
handle_cast({no}, State) -> {noreply, State}.
%% ====================================================================
%%handle_info
%% ====================================================================
handle_info({tcp, Socket, Bin}, Server_name)->
Msg=binary_to_term(Bin),
io:format("------------------receive client-------------------------------\n"),
io:format("msg:~p~n", [Msg]),
case Msg of
{register,{user,Name},{psw,Psw}}->
Request={register,{user,Name},{psw,Psw},{socket,Socket},{server_Name,Server_name}},
gen_server:call({?Server_Holl,?Node_Holl}, Request);
{login,{user,Name},{psw,Psw}}->
Request={login,{user,Name},{psw,Psw},{socket,Socket},{server_Name,Server_name}},
gen_server:call({?Server_Holl,?Node_Holl}, Request);
{get_all_users,{name,_Name}}->
Request={get_all_users,{socket,Socket},{server_Name,Server_name}},
gen_server:call({?Server_Chat,?Node_Chat}, Request);
{chat,{name,Name},{content,Content}}->
Request={chat,{name,Name},{content,Content},{server_Name,Server_name}},
gen_server:call({?Server_Chat,?Node_Chat}, Request);
{chat_group,{name,Name},{list,List},{msg,Content}}->
Request={chat_group,{name,Name},{list,List},{msg,Content},{server_Name,Server_name}},
gen_server:call({?Server_Chat,?Node_Chat}, Request);
_->
io:format("not reg \n")
end,
{noreply, Server_name};
handle_info({tcp_closed,Socket}, Server_name) ->
io:format("client: is quite~p~n",[Socket]),
Requset={quite,{socket,Socket},{server_name,Server_name}},
gen_server:call({?Server_Chat,?Node_Chat},Requset),
{noreply,Server_name}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State,_Extra) -> {ok, State}.
send(Name,[{To_Name,Socket}|T],Msg)->
Msg_info={{name,Name},{msg,Msg}},
io:format("--------------------------------------------\n"),
%% io:format("Name:~p~n",[Name]),
%% io:format("TO_Name:~p~n",[To_Name]),
case gen_tcp:send(Socket,term_to_binary(Msg_info)) of
ok ->io:format(" send ok-------~n form:~p~n to:~p~n msg:~p~n socket:~p~n ",
[Name,To_Name,Msg,Socket]);
_ ->io:format(" send error-------~n form:~p~n to:~p~n msg:~p~n socket:~p~n ",
[Name,To_Name,Msg,Socket])
end,
io:format("--------------------------------------------\n"),
send(Name,T,Msg);
send(_Name,[],_Msg)->
over.
解释:该模块使用的是gen_server的行为模式,该模式中的handle_info用来接收系统发来的消息(具体的不太理解,哪些是系统的消息)。可以用来接收从客户端发来的消息,相当于receive .......end 的作用。
欢迎转载,请注明出处
holl节点和chat节点我会在后面写到的,客户端的代码就不贴出来了,就是很简单的,定义消息的,在源码里在给大家吧~~