Erlang 用OTP写聊天室

这是一个关于如何使用Erlang的OTP构建聊天室的示例。代码包括客户端(chat_client)和服务器端(chat_server)的功能,如登录、注册、创建/加入/退出房间、房间内聊天和私聊等操作。客户端通过TCP连接与服务器通信,并使用ETS表存储用户信息。服务器端负责处理客户端请求,如登录验证、房间管理等。

聊天室代码

chat_client

module(chat_client).
-author(“Administrator”).
%% API
-export([start/0, login/2, reg/2,showuser/0, showroom/0,create_room/1,exit_room/0,join_room/1, send_msg/1, send_for/2, exit/0]).
-define(Port, 2345).

%%功能:登录/注册,创建/加入/退出房间,房间内公屏聊天,私聊,显示用户名/房间名/房间人数,退出账号

%%建立TCP连接,进程client打开一个和Server通信的套接字,建立ETS表用于存储用户信息
start() ->
register(client, spawn(fun() -> {ok, Socket} = gen_tcp:connect(“localhost”, ?Port, [binary, {packet, 4}]),
loop(Socket) end)),
ets:new(room, [public, set, named_table]),
ets:new(user, [public, set, named_table]),
{ok,please_login_or_register}.

%%%API函数

%%登录
login(ID, Password) ->
client ! {ID, Password, login}.

%%注册
reg(ID, Password) ->
client ! {ID, Password, register}.

%%显示房间
showroom()->
[{Roomname,Roomnumber}] = ets:tab2list(room),
io:format(“Room is pn”,[Roomname]),
io:format(“Numbers of room is pn”,[Roomnumber]).

%%显示用户名
showuser()->
[{ID,Socket}] = ets:tab2list(user),
io:format(“ID is:pn”,[ID]),
io:format(“Socket is:pn”,[Socket]).

%%创建房间
create_room(Roomname) ->
case ets:first(user) of
‘$end_of_table’ ->
io:format(“you must login first”);
_ ->
client ! {Roomname, create}
end.

%%加入房间
join_room(Room_name) ->
%%如果用户没登录
case ets:first(user) of
e n d o f t a b l e ′ − > i o : f o r m a t ( " y o u m u s t l o g i n f i r s t " ) ; − > c a s e e t s : f i r s t ( r o o m ) o f ′ end_of_table' -> io:format("you must login first"); _ -> case ets:first(room) of ' endoftable>io:format("youmustloginfirst");>caseets:first(room)ofend_of_table’ ->
client ! {Room_name, join};
_ ->
%%如果用户已经在房间里面
io:format(“you are in another room”)
end
end.

%%退出房间
exit_room() ->
case ets:first(room) of
‘$end_of_table’ ->
io:format(“you must join room first”);
_ ->
Room_name = ets:first(room),
client ! {Room_name, exit_room}
end.

%%房间内公屏发送消息
send_msg(Msg) ->
case ets:first(user) of
e n d o f t a b l e ′ − > i o : f o r m a t ( " y o u m u s t l o g i n f i r s t " ) ; − > c a s e e t s : f i r s t ( r o o m ) o f ′ end_of_table' -> io:format("you must login first"); _ -> case ets:first(room) of ' endoftable>io:format("youmustloginfirst");>caseets:first(room)ofend_of_table’ ->
io:format(“you must join room first”);
_ ->
client ! {Msg, msg}
end
end.

%%私聊
send_for(Msg, Who) ->
case ets:first(user) of
‘$end_of_table’ ->
io:format(“you must login first”);
_ ->
client ! {Msg, Who, one}
end.

%退出账号
exit() ->
case ets:first(user) of
‘$end_of_table’ ->
io:format(“you must login first”);
_ ->
Room_name = ets:first(room),
ID = ets:first(user),
client ! {Room_name, ID, closed}
end.

loop(Socket) ->
receive
{ID, Password, login} ->
gen_tcp:send(Socket, term_to_binary({ID, Password, login})),
receive
{tcp, _Socket, Bin} ->
CC = binary_to_term(Bin),
case CC of
login_succeed ->
ets:insert(user, {ID, Socket}),
io:format(“Receive : pn”, [CC]);
_ ->
io:format(“Receive : pn”, [CC])
end
end;
{ID, Password, register} ->
gen_tcp:send(Socket, term_to_binary({ID, Password, register})),
receive_loop();
{Msg, Roomname, msg} ->
gen_tcp:send(Socket, term_to_binary({Msg, Roomname, msg})),
receive_loop();
{Msg, Who, one} ->
gen_tcp:send(Socket, term_to_binary({Msg, Who, one})),
receive_loop();
{Roomname, create} ->
gen_tcp:send(Socket, term_to_binary({Roomname, create})),
receive_loop();
{Room_name, join} ->
gen_tcp:send(Socket, term_to_binary({Room_name, join})),
receive
{tcp, _Socket, Bin} ->
CC = binary_to_term(Bin),
case CC of
{join_succeed, Room_name, NewNumber} ->
ets:insert(room, {Room_name, NewNumber}),
io:format(“Receive : pn”, [CC]);
_ ->
io:format(“Receive : pn”, [CC])
end
end;
{Room_name, exit_room} ->
gen_tcp:send(Socket, term_to_binary({Room_name, exit_room})),
receive
{tcp, _Socket, Bin} ->
CC = binary_to_term(Bin),
case CC of
exit_succeed ->
ets:delete(room, Room_name),
io:format(“Receive : pn”, [CC]);
_ ->
io:format(“Receive : pn”, [CC])
end
end;
{Room_name, ID, closed} ->
gen_tcp:send(Socket, term_to_binary({Room_name, ID, closed})),
receive_loop();
{tcp, Socket, Bin} ->
AA = binary_to_term(Bin),
io:format(“Receive : pn”, [AA]);
{tcp_closed, Socket} ->
io:format(“closed now”)
end,
loop(Socket).

%%接收函数
receive_loop() ->
receive
{tcp, _Socket, Bin} ->
CC = binary_to_term(Bin),
io:format(“Receive : pn”, [CC])
end.

chat_server

-module(chat_server).
-author(“Administrator”).
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(Port, 2345).

start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [2345], []).

init([Port]) ->
%%创建aony和nkss两个房间
%%创建login、register和room三个表记录user数据
ets:new(room, [public, set, named_table]),
ets:new(aony, [public, set, named_table]),
ets:new(nkss, [public, set, named_table]),
ets:new(register, [public, set, named_table]),
ets:insert(room, [{nkss, 0}, {aony, 0}]),
main_start(Port),
{ok, ets:new(login, [public, set, named_table])}.

%%创建并行服务器
main_start(Port) ->
{ok, Listen} = gen_tcp:listen(Port, [binary, {packet, 4}]),
spawn(fun() -> main_connect(Listen) end).

main_connect(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
io:format(“connect succeed Socket = pn”, [Socket]),
spawn(fun() -> main_connect(Listen) end),
main(Socket).

main(Socket) ->
receive
{tcp, Socket, Bin} ->
Message = binary_to_term(Bin),
case Message of
{ID, Password, login} ->
login_handle(ID, Password, Socket);
{ID, Password, register} ->
register_handle(ID, Password, Socket);
{Msg, Roomname, msg} ->
send_handle(Msg, Socket, Roomname);
{Msg, Who, one} ->
one_for_one_handle(Msg, Who, Socket);
{Roomname, create} ->
create_handle(Roomname, Socket);
{Room_name, join} ->
join_handle(Room_name, Socket);
{Room_name, exit_room} ->
exit_room_handle(Room_name, Socket);
{Roomname, ID, closed} ->
closed_handle(Roomname, ID, Socket)
end
end,
main(Socket).

%%调用handle_call来处理客户端的需求
login_handle(ID, Password, Socket) ->
case gen_server:call(?MODULE, {ID, Password, Socket, login}) of
ok ->
gen_tcp:send(Socket, term_to_binary(login_succeed));
no ->
gen_tcp:send(Socket, term_to_binary(no_false))
end.

register_handle(ID, Password, Socket) ->
case gen_server:call(?MODULE, {ID, Password, register}) of
ok ->
gen_tcp:send(Socket, term_to_binary({ok, register_succeed}));
no ->
gen_tcp:send(Socket, term_to_binary({no, exist}))
end.

send_handle(Msg, Socket, Roomname) ->
List = ets:tab2list(Roomname),
AonyList = [X || {X, _Y} <- List],
gen_server:call(?MODULE, {Msg, Socket, AonyList, Roomname, msg}).

one_for_one_handle(Msg, Who, Socket) ->
gen_server:call(?MODULE, {Msg, Who, Socket, one_for_one}).

create_handle(Roomname, Socket) ->
gen_server:call(?MODULE, {Roomname, Socket, create}).

join_handle(Room_name, Socket) ->
case gen_server:call(?MODULE, {Room_name, Socket, join}) of
{ok, NewNumber} ->
gen_tcp:send(Socket, term_to_binary({join_succeed, Room_name, NewNumber}));
no ->
gen_tcp:send(Socket, term_to_binary(the_room_non_exitsent));
{no, you_are_in_room} ->
gen_tcp:send(Socket, term_to_binary({no, you_are_in_room}))
end.

exit_room_handle(Room_name, Socket) ->
gen_server:call(?MODULE, {Room_name, Socket, exit_room}).

closed_handle(Roomname, ID, Socket) ->
gen_server:call(?MODULE, {Roomname, ID, Socket, closed}).

%%% callbacks
%%% 处理客户端的需求
%%% 登录
handle_call({ID, Password, Socket, login}, _From, Tab) ->
Reply = case ets:lookup(register, ID) of
[{ID, Password}] ->
ets:insert(login, {ID, Socket}),
ok;
_ -> no
end,
{reply, Reply, Tab};

%% 注册
handle_call({ID, Password, register}, _From, Tab) ->
Reply = case ets:lookup(register, ID) of
[] ->
ets:insert(register, {ID, Password}),
ok;
_ -> no
end,
{reply, Reply, Tab};

%% 创建房间
handle_call({Roomname, Socket, create}, _From, Tab) ->
Reply = case ets:lookup(room, Roomname) of
[{Roomname, _Number}] ->
gen_tcp:send(Socket, term_to_binary({room_is_exist}));
_ ->
ets:new(Roomname, [public, set, named_table]),
ets:insert(room, {Roomname, 0}),
gen_tcp:send(Socket, term_to_binary({create_succeed}))
end,
{reply, Reply, Tab};

%% 加入房间
handle_call({Room_name, Socket, join}, _From, Tab) ->
Reply = case ets:lookup(room, Room_name) of
[{Room_name, RoomNumber}] ->
case ets:lookup(Room_name, Socket) of
[] ->
ets:insert(Room_name, {Socket, online}),
ets:insert(room, {Room_name, RoomNumber + 1}),
{ok, RoomNumber + 1};
[{Socket, online}] ->
{no, you_are_in_room}
end;
[] ->
no
end,
{reply, Reply, Tab};

%% 退出房间
handle_call({Room_name, Socket, exit_room}, _From, Tab) ->
Reply = case ets:lookup(room, Room_name) of
[{Room_name, RoomNumber}] ->
case ets:lookup(Room_name, Socket) of
[{Socket, online}] ->
ets:delete(Room_name, Socket),
ets:insert(room, {Room_name, RoomNumber - 1}),
gen_tcp:send(Socket, term_to_binary(exit_succeed));
[] ->
gen_tcp:send(Socket, term_to_binary(join_room_first))
end;
[] ->
gen_tcp:send(Socket, term_to_binary(no_this_room))
end,
{reply, Reply, Tab};

%% 退出账号
handle_call({Roomname, ID, Socket, closed}, _From, Tab) ->
Reply = case ets:lookup(login, ID) of
[{ID, Socket}] ->
ets:delete(login, ID),
[{Roomname, Number}] = ets:lookup(room, Roomname),
ets:insert(room, {Roomname, Number - 1}),
ets:delete(Roomname, Socket),
gen_tcp:send(Socket, term_to_binary(succeedexit));
_ ->
gen_tcp:send(Socket, term_to_binary(noexit))
end,
{reply, Reply, Tab};

%% 房间公屏聊天
handle_call({Msg, Socket, AonyList, Roomname, msg}, _From, Tab) ->
Reply = case ets:lookup(Roomname, Socket) of
[{Socket, online}] ->
lists:map(fun(E) -> gen_tcp:send(E, term_to_binary(Msg)) end, AonyList);
[] ->
gen_tcp:send(Socket, term_to_binary({no_in_room, Roomname}))
end,
{reply, Reply, Tab};

%% 私聊
handle_call({Msg, Who, Socket, one_for_one}, _From, Tab) ->
Reply = case ets:lookup(login, Who) of
[{Who, WhoSocket}] ->
gen_tcp:send(WhoSocket, term_to_binary(Msg)),
gen_tcp:send(Socket, term_to_binary(Msg));
[] ->
gen_tcp:send(Socket, term_to_binary(no_player_or_unonline))
end,
{reply, Reply, Tab}.

handle_cast(_Msg, State) ->
{noreply, State}.

handle_info(_Info, State) ->
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值