具有重新连接功能

具有重新连接功能

本文介绍了通过websocket的简单聊天系统,具有重新连接功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经看到了许多用erlang和cowboy实现的Websocket聊天室系统的例子。



我看到的大多数示例都使用gproc。实际上,每个websocket处理程序都会向gproc注册自己,然后广播/接收来自gproc的消息。处理程序gen_fsm,实际上广播/接收来自gproc的所有消息。这样,只要用户退出并仍然缓冲所有消息,gen_fsm便可以从已连接状态切换到已断开状态。过一会儿,如果用户不在线,则gen_fsm将终止。



这是一个好的解决方案吗?如何制作新的websocket处理程序以恢复gen_fsm进程?我应该使用用户名注册gen_fsm还是有更好的解决方案?

解决方案

我要做的是:

当用户连接到站点时,我生成了代表该用户的gen_server。然后,gen服务器将自己在gproc中注册为{n,l,{user,UserName}}。 (它可以注册{p,l,{chat,ChannelID}}之类的属性来收听聊天频道。(请参阅))



好,现在用户websocket连接启动了牛仔处理程序(我使用)。处理程序向gproc询问用户的gen_server的pid(),然后将自己注册为消息的接收者。因此,现在,当用户gen_server接收到消息时,它将消息重定向到websocket处理程序。



当websocket连接结束时,处理程序从用户gen_server进行注册,因此gen_server用户将保留消息,直到下一次连接或下一次超时为止。在超时时,您可以简单地终止服务器(消息将会丢失,但可以)。



请参阅:(未测试)

  -module(user_chat)。 

-record(状态,{mailbox,receiver = undefined})。

-export([start_link / 1,set_receiver / 1,unset_receiver / 1])。
%% API

start_link(UserID)->
gen_server:start_link(?MODULE,[UserID],[])。

set_receiver(UserID)->
set_receiver(UserID,self())。

unset_receiver(UserID)->
%%只需将接收器设置为未定义
set_receiver(UserID,undefined)。

set_receiver(UserID,ReceiverPid)->
UserPid = gproc:where({n,l,UserID}),
gen_server:call(UserPid,{set_receiver,ReceiverPid})。


%% Gen服务器内部

init([UserID])->
gproc:reg({n,l,{user,UserID}}),
{ok,#state {mailbox = []}}。

handle_call({set_receiver,ReceiverPid},_ From,#state {mailbox = MB} = State)->
NewMB = check_send(MB,State),
{reply,ok,State#state {receiver = ReceiverPid,mailbox = NewMB}}。

handle_info({chat_msg,Message},#state {mailbox = MB} = State)->
NewMB = check_send([Message | MB],State),
{noreply,State#state {mailbox = NewMB}}。

%%邮箱为空
check_send([],_)-> [];
%%收件人未定义,请保持邮件
check_send(Mailbox,#state {receiver = undefined})->邮箱
%%收件人是一个pid
check_send(Mailbox,#state {receiver = Receiver})is_pid(Receiver)->
%%发送所有消息
收件人! {chat_messages,Mailbox},
%%然后返回空邮箱
[]。


I have seen many examples of chat room systems over websocket implemented with erlang and cowboy.

Most of the examples I have seen use gproc. In practice each websocket handler registers itself with gproc and then broadcasts/receives messages from it.

Since a user could close by accident the webpage I am thinking about connecting to the websocket handler a gen_fsm which actually broadcasts/receives all the messages from gproc. In this way the gen_fsm could switch from a "connected" state to a "disconnected" state whenever the user exits and still buffer all the messages. After a while if the user is not back online the gen_fsm will terminate.

Is this a good solution? How can I make the new websocket handler to recover the gen_fsm process? Should I register the gen_fsm using the user name or is there any better solution?

解决方案

What i do is the folowing :

When an user connects to the site, i swpawn a gen_server reprensenting the user. Then, the gen server registers itself in gproc as {n,l, {user, UserName}}. (It can register properties like {p,l, {chat, ChannelID}} to listen to chat channels. (see gproc pub/sub))

Ok so now the user websocket connection starts the cowboy handler (i use Bullet). The handlers asks gproc the pid() of the user's gen_server and registrers itself as a receiver of messages. So now, when the user gen_server receives messages, it redirects them to the websocket handler.

When the websocket connexion ends, the handler uregister from the user gen_server, so the user gen_server will keep messages until the next connection, or the next timeout. At the timeout, you can simply terminate the server (messages will be lost but it is ok).

See : (not tested)

-module(user_chat).

-record(state, {mailbox,receiver=undefined}).

-export([start_link/1,set_receiver/1,unset_receiver/1]).
%% API

start_link(UserID) ->
    gen_server:start_link(?MODULE,[UserID],[]).

set_receiver(UserID) ->
    set_receiver(UserID,self()).

unset_receiver(UserID) ->
    %% Just set the receiver to undefined
    set_receiver(UserID,undefined).

set_receiver(UserID, ReceiverPid) ->
    UserPid = gproc:where({n,l,UserID}),
    gen_server:call(UserPid,{set_receiver,ReceiverPid}).


%% Gen server internals

init([UserID]) ->
    gproc:reg({n,l,{user,UserID}}),
    {ok,#state{mailbox=[]}}.

handle_call({set_receiver,ReceiverPid},_From,#state{mailbox=MB}=State) ->
    NewMB = check_send(MB,State),
    {reply,ok,State#state{receiver=ReceiverPid,mailbox=NewMB}}.

handle_info({chat_msg,Message},#state{mailbox=MB}=State) ->
    NewMB = check_send([Message|MB],State),
    {noreply, State#state{mailbox=NewMB}}.

%% Mailbox empty
check_send([],_) -> [];
%% Receiver undefined, keep messages
check_send(Mailbox,#state{receiver=undefined}) -> Mailbox
%% Receiver is a pid
check_send(Mailbox,#state{receiver=Receiver}) when is_pid(Receiver) ->
    %% Send all messages
    Receiver ! {chat_messages,Mailbox},
    %% Then return empty mailbox
    [].

这篇关于通过websocket的简单聊天系统,具有重新连接功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 01:08