erlang gen_tcp 聊天室(node节点)

自己写的一个简单的基于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节点我会在后面写到的,客户端的代码就不贴出来了,就是很简单的,定义消息的,在源码里在给大家吧~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Erlang中,我们可以使用`gen_tcp`模块来实现TCP客户端。下面是一个简单的例子,展示如何使用`gen_tcp`模块来连接到一个服务端: ```erlang -module(tcp_client). -export([start/1]). start(Host) -> {ok, Socket} = gen_tcp:connect(Host, 80, [binary, {packet, 0}]), gen_tcp:send(Socket, "GET / HTTP/1.0\r\n\r\n"), receive_data(Socket), gen_tcp:close(Socket). receive_data(Socket) -> case gen_tcp:recv(Socket, 0) of {ok, Data} -> io:format("~s", [Data]), receive_data(Socket); {error, closed} -> ok end. ``` 在上面的例子中,`gen_tcp:connect`函数用于连接到指定的服务端。`Host`参数是服务端的IP地址或域名,`80`是服务端的端口号。`[binary, {packet, 0}]`选项说明我们要传输二进制数据,且数据包不需要打包。 一旦连接建立成功,我们就可以使用`gen_tcp:send`函数向服务端发送数据了。在这个例子中,我们发送了一个HTTP GET请求。接着,我们调用了`receive_data`函数来接收服务端返回的数据。`gen_tcp:recv`函数用于接收服务端发送的数据。`0`参数表示接收的数据包没有大小限制。如果接收到数据,我们就打印出来,并继续调用`receive_data`函数来接收更多的数据。如果服务端关闭了连接,`gen_tcp:recv`函数会返回错误信息`{error, closed}`,我们就可以关闭连接了。 在使用`gen_tcp`模块时,我们需要注意以下几点: - 为了避免阻塞,在发送和接收数据时,我们可以使用`gen_tcp:send/2`和`gen_tcp:recv/2`函数的阻塞模式,即将第二个参数设置为`0`。这样,如果没有数据可发送或接收,函数就会马上返回。 - 如果我们需要发送和接收大量的数据,应该使用`{packet, N}`选项来打包数据。这个选项会将数据分成大小为N的块,并且在每个块前面添加4个字节的头部,用于表示这个块的大小。这样可以避免粘包问题。 - 在发送和接收数据时,我们应该按照服务端和客户端的协议来组织数据包。如果协议有变化,我们也需要修改代码来适应新的协议。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值