简体   繁体   English

根据erlang中List的值从记录中查找元组

[英]Find Tuple from Record base on value of List in erlang

I am developing chat application using Erlang where I want to store name while creating client, Right now I am just store Pid. 我正在使用Erlang开发聊天应用程序,我想在创建客户端时存储名称,现在我只是存储Pid。 I am using Record to store Value. 我正在使用记录来存储价值。 I want to fetch name when client send message, So I have to find name from Pid. 我想在客户端发送消息时获取名称,因此我必须从Pid中查找名称。 I am using erlang 17. 我正在使用erlang 17。

chat_room.erl chat_room.erl

-module(chat_room).
-behaviour(gen_server).

-export([start_link/0, enter/2, leave/1, send_message/2, find_user/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
         code_change/3]).

-define(SERVER, ?MODULE).

-record(state, {clients=[],name}).

%%%=============================================================================
%%% API
%%%=============================================================================

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

enter(Pid, Name) ->
    gen_server:cast(?SERVER, {enter, Pid, Name}).

leave(Pid) ->
    gen_server:cast(?SERVER, {leave, Pid}).

send_message(Pid, Message) ->
    gen_server:cast(?SERVER, {send_message, Pid, Message}).


find_user(Pid) ->
        gen_server:cast(?SERVER, {find_user, Pid}).

%%%=============================================================================
%%% gen_server callbacks
%%%=============================================================================

init([]) ->

    Dispatch = cowboy_router:compile([
        {'_', [

               {"/ws", chat_ws_handler, []},

               {"/", cowboy_static,
                [{directory, {priv_dir, chat, [<<"static">>]}},
                 {file, <<"index.html">>},
                 {mimetypes, {fun mimetypes:path_to_mimes/2, default}}]},

               {"/static/[...]", cowboy_static,
                [{directory, {priv_dir, chat, [<<"static">>]}},
                 {mimetypes, {fun mimetypes:path_to_mimes/2, default}}]}

              ]}
    ]),

    cowboy:start_http(chat, 100,
                      [{port, 8080}],
                      [{env, [{dispatch, Dispatch}]}]),
    {ok, #state{}}.

handle_call(_Request, _From, State) ->
    {noreply, State}.
handle_cast({enter, Pid, Name}, State = #state{clients= Clients}) ->
    {noreply, State#state{clients = [Pid|Clients],name=Name}};
handle_cast({leave, Pid}, State = #state{clients = Clients}) ->
    {noreply, State#state{clients  = Clients -- [Pid]}};

handle_cast({find_user, Pid}, State= #state{clients = Clients}) ->
        io:format("List: ~p", [State]),
        {noreply, State};

handle_cast({send_message, Pid, Message}, State) ->
    do_send_message(Pid, Message, State),
    {noreply, State}.

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

terminate(_Reason, _State) ->
    cowboy:stop_listener(chat).

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

%%%=============================================================================
%%% Internal functions
%%%=============================================================================

do_send_message(Pid, Message, #state{clients = Clients}) ->
    OtherPids = Clients -- [Pid],
    lists:foreach(
      fun(OtherPid) ->
              OtherPid ! {send_message, self(), Message}
      end, OtherPids).

If I used existing structure, how can I find name from Pid? 如果使用现有结构,如何从Pid中查找名称?

Using the existing structure, I guess you'd send a message to a client-pid asking for its name and write a handler there to reply. 使用现有结构,我想您会向client-pid发送一条消息,询问其名称,然后在其中编写一个处理程序以进行回复。

But it seems sensible to have the server store a name-to-pid mapping and to send your messages by name instead of by pid. 但是,让服务器存储名称到pid的映射并按名称而不是pid发送消息似乎是明智的。

I recently implemented a similar application myself ( https://github.com/huseyinyilmaz/talkybee and for backend https://github.com/huseyinyilmaz/publicator ) And my process structure was like this. 我最近亲自实现了一个类似的应用程序( https://github.com/huseyinyilmaz/talkybee和后端https://github.com/huseyinyilmaz/publicator ),我的流程结构就是这样。

[cowboy connection processes] -> [user_processes] -> [room_processes] [牛仔连接过程]-> [用户进程]-> [房间进程]

So if you use similar structure you can hold names on user processes (one process for every user) and when you need name of a user you can just ask the process its name. 因此,如果您使用类似的结构,则可以在用户进程中保留名称(每个用户一个进程),当您需要用户名时,您可以询问该进程的名称。

Just to be clear: 只是要清楚:

  • Your chat_room module represents all rooms (you might want to seperate cowboy initialization and create one process for each room chat_room模块代表所有房间(您可能希望单独进行牛仔初始化,并为每个房间创建一个进程
  • Cowboy connection processes are created by cowboy and reused. 牛仔连接过程由牛仔创建并重用。 So you cannot just use connection processes as users. 因此,您不能仅将连接过程用作用户。 (unless you are doing a websocket connection) (除非您正在进行websocket连接)
  • You have to create a new module named chat_user that represents a chat user and store user level data on its state. 您必须创建一个名为chat_user的新模块,该模块代表一个聊天用户,并在其状态下存储用户级别的数据。

I hope that helps. 希望对您有所帮助。

Here is what I would do for enter and leave . 这里是我会做的enterleave I'll leave find_user and do_send as an exercise (hint: see lists:keyfind). 我将把find_userdo_send练习(提示:请参见lists:keyfind)。

handle_cast({enter, Pid, Name}, State = #state{clients= Clients}) ->
    {noreply, State#state{clients = [{Pid, Name} | Clients]}};

handle_cast({leave, Pid}, State = #state{clients = Clients}) ->
    {noreply, State#state{clients  = lists:keydelete(Pid, 1, Clients)}};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM