一个自定义通讯协议的erlang socket多人在线聊天程序
2017-03-28 23:36
393 查看
利用自定义的erlang通讯协议编写的入门socket程序,可实现简单的多人在线聊天程序。
服务端的代码:
2.客户端的代码
客户端与服务端的通讯协议为自定义形式,协议号占4位,数据部分分为两部分,一部分为个人姓名,另一部分为信息,如登录密码或者聊天信息,或者登出标志。如:
封包时的协议:<<0000:4,byte_size(Name):16,Name/binary,byte_size(Msg):16,Msg/binary>>
服务端的代码:
-module(server_example). -export([start/0,initialize_ets/0,info_lookup/1,loop/2,info_update/3,init/1,handle_call/3,terminate/2]). -import(counter,[start/1,add/1,value/1,decrease/1,log_add/1,chat_add/1]). -import(my_fsm,[start_count/0,count/1]). -include("user_info.hrl"). -define(SERVER,?MODULE). start() -> gen_server:start_link({local,?SERVER},?MODULE,[],[]). init([]) -> initialize_ets(), start_parallel_server(), {ok,true}. user_online_count({add,Pid}) -> gen_server:call(?MODULE,{add,Pid}). user_online_decrease({decrease,Pid}) -> gen_server:call(?MODULE,{decrease,Pid}). personal_login_count({ladd,Name,Count}) -> gen_server:call(?MODULE,{ladd,Name,Count}). personal_chat_count({cadd,Name,Count}) -> gen_server:call(?MODULE,{cadd,Name,Count}). %统计在线用户的回调函数 handle_call({add,Pid},_From,State) -> counter:add({add,Pid}), Reply = counter:value(Pid), {reply,Reply,State}; handle_call({decrease,Pid},_From,State) -> counter:decrease(Pid), Reply = counter:value(Pid), {reply,Reply,State}; %统计个人登录次数 handle_call({ladd,Name,Count},_From,State) -> Pi = spawn(fun() -> counter:start({ladd,Count}) end), counter:add({ladd,Pi}), Reply = counter:log_add({value,Pi}), info_update(Name,4,Reply), {reply,Reply,State}; %个人来聊天次数 handle_call({cadd,Name,Count},_From,State) -> C = spawn(fun() -> counter:start({cadd,Count}) end), counter:add({cadd,C}), Reply = counter:chat_add({value,C}), info_update(Name,5,Reply), {reply,Reply,State}. terminate(_Reason,_State) -> ok. %开启服务器 start_parallel_server() -> {ok,Listen} = gen_tcp:listen(2345,[binary,{packet,4},{reuseaddr,true},{active,true}]), Pid = spawn(fun() -> counter:start({init,0}) end),%开启统计进程,此时为0 my_fsm:start_count(),%统计某时段的登录数 spawn(fun() -> per_connect(Pid,Listen) end). %每次绑定一个当前Socket后再分裂一个新的服务端进程,再接收新的请求 per_connect(Pid,Listen) -> {ok,Socket} = gen_tcp:accept(Listen), spawn(fun() -> per_connect(Pid,Listen) end), loop(Pid,Socket). %初始化ets initialize_ets() -> ets:new(test,[set,public,named_table,{keypos,#user.name}]), ets:insert(test,#user{id=01,name=laner,passwd="123456",login_times=0,chat_times=0,last_login={}}), ets:insert(test,#user{id=02,name=river,passwd="23456",login_times=0,chat_times=0,last_login={}}), ets:insert(test,#user{id=03,name=huang,passwd="3456",login_times=0,chat_times=0,last_login={}}). %查询ets info_lookup(Key) -> %返回值是一个元组 ets:lookup(test,Key). %修改ets信息 info_update(Key,Pos,Update) -> ets:update_element(test,Key,{Pos,Update}). %接收信息并处理 loop(Pid,Socket) -> io:format("receiving...~n"), receive {tcp,Socket,Bin} -> <<State:4,Str1:2/binary,Str2:2/binary>> = Bin, %case binary_to_term(Bin) of case State of %登录 0000 -> %{Name,Passwd} = binary_to_term(Str), Name = binary_to_term(Str1), case info_lookup(Name) of [{user,Uid,Pname,Pwd,Logc,ChatC,Lastlog}] -> S = term_to_binary(success), N = term_to_binary(Name), Packet = <<0000:4,(byte_size(S)):16,S/binary,(byte_size(N)):16,N/binary>>, gen_tcp:send(Socket,Packet), my_fsm:count(0), %取到了名字和密码之后发送成功的标识并增加登录次数 Reply = user_online_count({add,Pid}), io:format("~p users online ~n",[Reply]), Reply1 = personal_login_count({ladd,Name,Logc}), io:format("user ~p have logged ~p times ~n",[Name,Reply1]), loop(Pid,Socket); %为空表示该用户没有记录 [{}] -> io:format("you haved not registered yet"), F = term_to_binary(failed), N = term_to_binary(Name), Packet = <<0000:4,(byte_size(F)):16,F/binary,(byte_size(N)):16,N/binary>>, gen_tcp:send(Socket,Packet), loop(Pid,Socket) end; %接收信息 0001 -> Name = binary_to_term(Str1), Msg = binary_to_term(Str2), [#user{chat_times=Ccount}] = info_lookup(Name), Reply = personal_chat_count({cadd,Name,Ccount}), io:format("User ~p :~p~n",[Name,Msg]), io:format("User ~p have chatted with his friend on line ~p times ~n",[Name,Reply]), N = term_to_binary({ok,received}), Len = byte_size(N), Packet = <<0001:4,Len:16,N/binary>>, gen_tcp:send(Socket,Packet), loop(Pid,Socket); %退出 0002 -> Name = binary_to_term(Str2), io:format("~p~n",[info_lookup(Name)]), [#user{login_times=Logc,last_login=LastLo}] = info_lookup(Name), Last = calendar:now_to_local_time(erlang:now()), Reply = user_online_decrease({decrease,Pid}), N = term_to_binary(ok), Packet = <<0002:4,(byte_size(N)):16,N/binary>>, gen_tcp:send(Socket,Packet), %修改最后登录时间 info_update(Name,6,Last), io:format("last time ~p logined is ~p~n",[Name,Last]), io:format("~p users online~n",[Reply]) end; {tcp_closed,Socket} -> io:format("Server socket closed~n") end.
2.客户端的代码
-module(client_example). -export([get_socket/0,client_login/1,send_message/1,logout/1]). %获取socket get_socket() -> {ok,Socket} = gen_tcp:connect("localhost",2345,[binary,{packet,4}]), register(client,spawn(fun() -> handle(Socket) end)). %登录接口 client_login({Name,Password}) -> client ! {self(),{login,Name,Password}}, receive Response -> ok end. %聊天发送接口 send_message({Name,Msg}) -> client ! {self(),{msg,Name,Msg}}, receive Response -> ok end. %退出接口 logout(Name) -> client ! {self(),{logout,Name}}, receive Response -> ok end. handle(Socket) -> receive %来自控制进程的请求 {From,Request} -> case Request of %登录的请求协议号0000 {login,Name,Password} -> N = term_to_binary(Name), P = term_to_binary(Password), Packet = <<0000:4,(byte_size(N)):16,N/binary,(byte_size(P)):16,P/binary>>, gen_tcp:send(Socket,Packet), receive %来自服务端socket响应 {tcp,Socket,Bin} -> <<State:4,S:2/binary,N:2/binary>> = Bin, case binary_to_term(S) of success -> From ! {"you have login successfully"}, io:format("you have login successfully ~n"); failed -> From ! {"you haved login failed,please try again"}, gen_tcp:close(Socket) end after 5000 -> ok end, handle(Socket); %发送信息协议号0001 {msg,Name,Msg} -> io:format("my message:~p~n",[Msg]), N = term_to_binary(Name), M = term_to_binary(Msg), Packet = <<0001:4,(byte_size(N)):16,N/binary,(byte_size(M)):16,M/binary>>, gen_tcp:send(Socket,Packet), receive {tcp,Socket,Bin} -> <<State:4,N:2/binary>> = Bin, case binary_to_term(N) of {ok,received} -> From ! {"ok,you can send next message ~n"} end after 3000 -> ok end, handle(Socket); %登出协议号0002 {logout,Name} -> L = term_to_binary({logout}), N = term_to_binary({Name}), Packet = <<0002:4,(byte_size(L)):16,L/binary,(byte_size(N)):16,N/binary>>, gen_tcp:send(Socket,Packet), receive {tcp,Socket,Bin} -> <<State:4,N:2/binary>> = Bin, case binary_to_term(N) of ok -> From ! {"ok,you will logout successfully ~n"} end after 5000 -> ok end, gen_tcp:close(Socket) % 关闭socket时只有再次连接新socket才能打开 end end.
客户端与服务端的通讯协议为自定义形式,协议号占4位,数据部分分为两部分,一部分为个人姓名,另一部分为信息,如登录密码或者聊天信息,或者登出标志。如:
封包时的协议:<<0000:4,byte_size(Name):16,Name/binary,byte_size(Msg):16,Msg/binary>>
相关文章推荐
- [C#] Socket 通讯,一个简单的聊天窗口小程序
- 在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议)
- Linux下socket异步通讯聊天程序
- 一个简单的基于socket的通讯处理程序
- 在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议)
- 一个简单的自定义通信协议(socket)
- Linux下socket异步通讯聊天程序(转)
- 黑马程序员 用UDP协议在dos命令行里模拟一个聊天程序
- 【Java网络编程】UDP协议实例——简单的在线聊天程序
- 一个简单的自定义通信协议(socket)
- C# Socket聊天程序(一个服务端,多个客户端)
- 自定义网络通讯协议 socket通讯时都是走字符序列,注意字符串长度
- [dika 记录] cowboy 扩展socket 协议, 以解决flash ,erlang通讯的沙箱为例子
- Java编程:使用Socket编写一个能多个人聊天的程序
- 一个简单的socket通信聊天程序
- C# Socket聊天程序(一个服务端,多个客户端)
- 利用TCP传输协议实现基于Socket的聊天程序
- Socket编程 一个小的聊天程序
- 在C++ Builder中用socket api来写网络通讯程序(同时支持TCP和UDP协议)
- socket实现的一个基本点对点聊天程序