I'm trying to create a chatroom with cowboy websocket handler. I want that messages those come from every will be forwarded to other sockets as well, just like a chat group. I don't have any idea how to implement this? And I don't know how to save sockets that are connected to websocket so we can send messages to them. I have this cowboy handler:
-module(chat_conn).
-export([ init/2
, websocket_init/1
, websocket_handle/2
, websocket_info/2
, terminate/3
]).
-include("chat.hrl").
init(Req, [WsConnIdleTimeout]) ->
?LOG_DEBUG("[CHAT-CONN] New HTTP Request on: /api , Pid: ~p", [self()]),
WsOpts = #{idle_timeout => WsConnIdleTimeout},
State = #{counter => 0},
{cowboy_websocket, Req, State, WsOpts}.
websocket_init(State0) ->
?LOG_DEBUG("[FOOZI-CONN] HTTP Upgraded to WebSocketm pid: ~p", [self()]),
NewState = State0,
{ok, NewState}.
websocket_handle({text, PlainRequest}, #{counter := Counter0} = State) ->
?LOG_DEBUG("[HIGGS-CONN] Receive New Message: ~p, Pid: ~p" , [PlainRequest, self()]),
NewCounter = Counter0 + 1,
Reply = list_to_binary("Counter is: " ++ integer_to_list(NewCounter)),
NewState = #{counter => NewCounter},
{reply, {text, Reply}, NewState};
%{ok, State};
websocket_handle(Frame, State) ->
?LOG_INFO("[HIGGS-CONN] Invalid Frame => ~p", [Frame]),
{stop, State}.
websocket_info(Message, State) ->
?LOG_INFO("[CONN-INFO] Unhandled message! => Message: ~p", [Message]),
{reply, {text, list_to_binary(Message)}, State}.
%{ok, State}.
terminate(Reason, _, State) ->
?LOG_INFO("[CONN-TERMINATE] Terminated! => Pid: ~p, Reason: ~p, State: ~p", [self(), Reason, State]),
ok.
In erlang, gen_servers can be used to store state, so you can create a
gen_serverthat the websocket handlers employ to save the client pids. When a client sends a "start chat" message to your cowboy server using a specific route, the associated websocket handler will be called. Inside that handler,self()will be the pid of the websocket process for that client. You can save that pid in yourgen_serverby callinggen_server:cast(chat_room, {arrive, self()}), then insidechat_room:handle_cast()adding the pid to the list of pids stored in theStatevariable.When the cowboy server receives a chat message from a client browser, the client's websocket process will handle the message, and inside the appropriate websocket handler you can query the
gen_serverto get a list of the pids for the connected clients. Then you can use!to send each of them the message. In turn, each client'swebsocket_info()handler will handle the message, which it can relay through the websocket back to the client by returning:{reply, {text, Text}, State}.For your
gen_server, you will also need to implement aleave()function that the appropriate websocket handler will call to update the list of clients stored in thegen_server, e.g.gen_server:cast(chat_room, {leave, self()}).