服務端
點選檢視程式碼
%%%-------------------------------------------------------------------
%%% @author wujj
%%% @copyright (C) 2021, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 08. 8月 2021 13:33
%%%-------------------------------------------------------------------
-module(chatserv).
-author("wujj").
%% API
-compile(export_all).
-import(ets, [insert_new/2]).
start_server() ->
ets:new(id, [ordered_set, public, named_table, {write_concurrency, true}, {read_concurrency, true}]),
case gen_tcp:listen(1234, [binary, {packet, 0}, {active, true}]) of
{ok, ListenSocket} ->
spawn(fun() -> client_connect(ListenSocket) end);
{error, Reason} ->
io:format("~p~n", [Reason])
end.
client_connect(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
%% 進行驗證,看是否是註冊還是登入
spawn(fun() -> client_connect(ListenSocket) end),
loop(Socket);
{error, Reason} ->
io:format("~p~n", [Reason])
end.
loop(Socket) ->
receive
{tcp, Socket, Bin} ->
[Id, Sign, PassWord, SendId, MessageInfos] = binary_to_term(Bin),
if
Sign =:= register_user ->
Info = register_user(Id, PassWord, Socket),
gen_tcp:send(Socket, term_to_binary(Info)),
loop(Socket);
Sign =:= login_user ->
Info = login_user(Id, PassWord, Socket),
gen_tcp:send(Socket, term_to_binary(Info)),
loop(Socket);
Sign =:= login_out ->
Info = login_out(Id, Socket),
gen_tcp:send(Socket, term_to_binary(Info)),
loop(Socket);
Sign =:= private_msg ->
private_chat(SendId, Socket, MessageInfos),
loop(Socket);
Sign =:= group_msg ->
group_chat(Socket, MessageInfos),
loop(Socket);
true ->
io:format("error sign ~n"),
loop(Socket)
end;
{tcp_closed, Socket} ->
io:format("Server socket closed ~n")
end.
%% 使用者註冊
register_user(Id, PassWord, Socket) ->
case ets:lookup(id, Id) of
[_Ok] ->
io:format("Account is fail ~n"),
"Account is exist ~n";
_ ->
ets:insert(id, {Id, PassWord, 0, Socket}),
"register successed ~n"
end.
%% 使用者登入
login_user(Id, PassWord, Socket) ->
case ets:match_object(id, {Id, PassWord, 0, Socket}) of
[_Ok] ->
ets:update_element(id, Id, [{3, 1}, {4, Socket}]),
"login successed";
Reson ->
io:format("login is fail ~n ~p", [Reson]),
"Password error or Account is not exist ~n"
end.
%% 退出使用者
login_out(Id, Socket) ->
%% 因為id對應唯一socket,所以不需要PassWord
case ets:match_object(id, {Id, '_', 1, Socket}) of
[_Ok] ->
ets:update_element(id, Id, [{3, 0}, {4, 0}]),
"login successed";
_ ->
io:format("out is fail ~n"),
"login is fail"
end.
%% 群聊
group_chat(Socket, MessageInfos) ->
case ets:match_object(id, {'_', '_', 1, Socket}) of
[{Id, _, _, _}] ->
Res = ets:match_object(id, {'_', '_', 1, '_'}),
case Res =:= [] of
true ->
io:format("no person online ~p ~n", [Res]);
_ ->
group_send_msg(Res, Id, MessageInfos)
end;
_ ->
io:format("group chat is fail ~n")
end.
%% 群聊傳送
group_send_msg([], _Id, _MessageInfos) ->
next;
group_send_msg([Info | Infos], Id, MessageInfos) ->
{_, _, _, Socket} = Info,
gen_tcp:send(Socket, term_to_binary("from: " ++ integer_to_list(Id) ++ "say: " ++ MessageInfos)),
group_send_msg(Infos, Id, MessageInfos).
%% 線上私聊
private_chat(SendId, Socket, MessageInfos) ->
case ets:match_object(id, {'_', '_', 1, Socket}) of
[{Id, _, _, _}] ->
Res = ets:match_object(id, {SendId, '_', 1, '_'}),
case Res =:= [] of
true ->
io:format("send person not online ~p ~n", [Res]);
_ ->
private_send_msg(Res, Id, MessageInfos)
end;
_ ->
io:format("private chat is fail ~n")
end.
%% 私聊傳送
private_send_msg([Info], Id, MessageInfos) ->
{_, _, _, Socket} = Info,
gen_tcp:send(Socket, term_to_binary("from: " ++ integer_to_list(Id) ++ "say: " ++ MessageInfos)).
點選檢視程式碼
%%%-------------------------------------------------------------------
%%% @author wujj
%%% @copyright (C) 2021, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 08. 8月 2021 14:03
%%%-------------------------------------------------------------------
-module(chatclient).
-author("wujj").
%% API
-compile(export_all).
%客戶端
start_client() ->
{ok, Socket} = gen_tcp:connect("localhost", 1234, [binary, {packet, 0}]), %連線伺服器
%新建一個程序負責接收訊息
Pid = spawn(fun() -> loop() end),
gen_tcp:controlling_process(Socket, Pid),
sendMsg(Socket).
loop() ->
receive
{tcp, _Socket, Bin} ->
Res = binary_to_term(Bin),
io:format("Message Info! ~p ~n", [Res]),
loop();
{tcp_closed, _Socket} ->
io:format("Socket is closed! ~n")
end.
sendMsg(Socket) ->
S = io:get_line("select operation: "),
{Sign, _Info} = string:to_integer(S),
SendMsg = operation_message(Sign),
gen_tcp:send(Socket, term_to_binary(SendMsg)),
sendMsg(Socket).
%% 使用者註冊
operation_message(1) ->
I = io:get_line("id: "),
{Id, _Info} = string:to_integer(I),
Password = io:get_line("register password: "),
[Id, register_user, Password, 0, 0];
%% 使用者登入
operation_message(2) ->
I = io:get_line("id:"),
Password = io:get_line("login password: "),
{Id, _Info} = string:to_integer(I),
[Id, login_user, Password, 0, 0];
%% 使用者退出
operation_message(3) ->
I = io:get_line("id: "),
{Id, _Info} = string:to_integer(I),
[Id, login_out, 0, 0, 0];
%% 私聊
operation_message(4) ->
Sd = io:get_line("send_id: "),
Msg = io:get_line("MsgInfo: "),
{SendId, _Info} = string:to_integer(Sd),
[0, private_msg, 0, SendId, Msg];
%% 群聊
operation_message(5) ->
Msg = io:get_line("MsgInfo: "),
[0, group_msg, 0, 0, Msg];
%% 無效操作
operation_message(_) ->
Msg = io:format("invalid_operation ~n"),
[0, invalid_operation, 0, 0, Msg].