erlang聊天室

酷i發表於2024-06-25

服務端

點選檢視程式碼
%%%-------------------------------------------------------------------
%%% @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].

相關文章