I have a gen_server speaking to several hardware sensors. This setup works fine. Now I want to extend this system with a real time visualization of the data. And I want to deliver this data to a web client over websockets.
As I might have several "listeners", e.g. several people visualizing this data on different web browsers, I am thinking of something resembling the Observer Pattern. Each time a web client asks to subscribe to a sensor, it should be added to a list of stakeholders for that sensor. As soon as new sensor data arrives, it should be pushed out to the client without delay.
I am using yaws to quickly get websocket functionality. My problem is related to the way that yaws seems to work. On the server side I only "see" the client at connection time through the A#arg.clisock value (e.g. #Port<0.2825>). In the code below I register ws_server to receive the callbacks when new data enters from the client. After this point yaws seems to only allow me to respond to messages entering server side.
out(A) ->
CallbackMod = ws_server,
Opts = [{origin, "http://" ++ (A#arg.headers)#headers.host}],
{websocket, CallbackMod, Opts}.
This is what the callback module looks like:
% handle_message(Incoming)
% Incoming :: {text,Msg} | {binary,Msg} | {close, Status, Reason}
handle_message({Type,Data}) ->
gen_server:cast(?SERVER,{websocket,Data}),
noreply.
Nowhere, it seems, am I able to react to a message such as subscribe <sensor> and (after connection time) dynamically add this stakeholder to a list of observers.
How can I use the yaws server to accomplish asynchronous pushing of data to client and during session add and remove sensors that I want to listen to. Basically the easiest way would be if yaws could call back handle_message/2 with first argument being From. Otherwise I need to add a ref to keep a ref on both sides and send that to server each time, it seems backwards that I need to keep that information.
When the client starts a websocket connection Yaws will start a new gen_server process to handle all communication between client and server.
The gen_server dispatches all client-sent messages to your callback function handle_message/1, so it is not used only for the initial connect.
Also, the gen_server process is used to push data from the server to the client - use the yaws_api:websocket_send(Pid, {Type, Data}) function. The Pid parameter is the pid of the websocket gen_server. Your callback module could be changed to:
handle_message({Type,Data}) ->
gen_server:cast(?SERVER,{self(), Data}),
noreply.
This gives your server the pid of the websocket gen_server to be used with yaws_api:websocket_send/2. The Data parameter contains the client request and needs to be parsed by your server to check for sensor subscription requests and associate the websocket pid with the appropriate sensor.
Related
I am working on a project like whatsapp and using ejabberd as a backend server with Android/IOS clients. I want to do some whatsapp like step-
1. Client sends mobile number to server.
2. server sends OTP in return to the client and starts a timer say 2 minutes.
3. If client sends correct OTP, received from the server, to the server within the time specified. Client will be registerd.
I need help in which ejabberd module should I write the code of above steps. I know, to modify Ejabberd I can use Hooks and IQ handlers, but they can be used once the user is already registered. Right?
Should I use other language server, just for the above steps? Please help.
You should write new ejabberd module which starts a new SSL server on a specific port. Do your authentication on that.
If you are familiar with ejabberd's core, you can add your own module in listen part of config and write new module as backend of your server (same as ejabberd itself which wrote ejabberd_c2s, ejabberd_service, etc).
I just read its code and i recommend to read the code too.
For example in ejabberd version 17.01 here reads config and runs a tcp server for every section of listen key. every section has three parts Port, Module and Opts. For xmpp clients of ejabberd these are 5222, ejabberd_c2s and Opts is other values. in here for every accepted connection it calls ejabberd_socket:start(Module, gen_tcp, Sock, Opts). In ejabberd_socket:start/4 here starts a process say A for receiving from socket and parsing XML and send them to another process say B and here starts a process (B) from ejabberd_c2s. Process B receives XMLs from Process A and does all actions off XMPP clients in ejabberd.
I think you should create a restful service to do this.
Please consider following steps:
Client sends mobile number to restful service.
The restful service create a OTP and save it in Redis with expired is set.
The client sends OTP to restful service. If the OTP is correct, service sends a jwt Token to client and save it in Redis with expired is set.
The client sends jwt token to eJabberd server and the server authenticates with ejabberd_auth_jwt module.
This may meet your requirement.
I have just started off on Erlang. I want to create a TCP server in Erlang. My TCP client runs on Android and connects to the server.
I have taken the TCP server implementation from
https://github.com/kevinlynx/erlang-tcpserver
I am able to run the server, connect the client to it and send messages from the client to the server. Also, the logic in the server is that whenever it receives a messages from the client, it sends back the same message to the client.
All this works fine, my only problem is how do I send a message to client from the erlang shell(without having to wait for a message from client). The gen_tcp:send() function requires as input the Socket handle, whenever client sends a message, there is a callback and it has the socket handle so it can be used to send message back to the client, but how to do it otherwise?
On the server side, you must be accepting the connection somewhere:
{ok, Sock} = gen_tcp:accept(LSock)
And I suppose you could send a message to that socket:
gen_tcp:send(Sock, YourPacket)
If you do not accept connections then it is not a server.
Updating to answer comment
One way is sharing the listener socket (LSock in the example). You could save it on an accessible ETS and call the acceptor from the shell despite it not been the owner of the listener.
Otherwise you are going to have to wrap everything on a server where you keep the opened socket/s in a State, and program a handle to send messages to opened sockets. A nice explanation of a socket server can be found here.
I have just started off on Erlang. I want to create a TCP server in Erlang.
I think the problem is that you are using software that sets up a communication channel between a client and a sever:
(client) ================== (server)
Now you have a third entity:
(client) ================== (server)
(erlang shell)
and you want the erlang shell to communicate with the client. That's all well and good, but the code you are using doesn't provide for that. It sounds like you also want your client to act as a server for the erlang shell. Why do you need the erlang shell to send messages to the client?
The erlang shell could become a second client of the server:
(client) ================== (server)
(erlang shell) ============ (server)
but that doesn't help the erlang shell communicate directly with the client. The erlang shell could send some information to the sever, and the server could store that information in the State, then the server could pass the State to the client the next time the client made a request.
If the erlang shell had the Pid of the client, the erlang shell could always do:
Pid ! "hello client"
and if the client were waiting in a receive-statement, then the client could extract that message from its mailbox. What does your client look like?
I have a server with a number of clients and each client is able to ask the server for information about the other clients. If they do so, the server have to get the information from each client and then return it to the asking client.
If two clients does this request at the same time, a deadlock might appear. The thing is that this request is done so often that the client would not have to care if it sometimes fails. How do I just ignore the timeout message that terminates everything when this problem appear?
Strict answer to your question
If you're using gen_server, then call/3 allows you to specify a timeout (and call/2 defaults to 5 seconds).
This code will either give you the gen_server's reply or the atom timeout if it failed.
Result = try gen_server:call(Target, Message, Timeout) of
Reply ->
Reply
catch
exit:{timeout, _} ->
timeout
end.
Better answer
evnu and rvirding recommended using asynchronous calls, which is a superior technique. Here are two possible ways to do this:
1. Server stores the data
Have clients periodically gen_server:cast/2 to the server to tell it their information. The server stores the latest information about each client. When a client wants to learn about its siblings, it calls gen_server:call/2 to the server.
The server call is synchronous because it doesn't need to contact any client; it's just returning the cached values.
2. Async return
The clients call gen_server:cast/2 to request data from the server. The server calls gen_server:call/2 to fetch data from each client on demand. Once the server has collected all data, it calls gen_server:cast/2 to pass the collected data back to the client that requested it.
Here, the clients are always waiting to handle requests from the server. The server calls the client synchronously, but can't deadlock because there is only one server.
3. More gen_servers
This one's hard to describe without knowing more about your code, but you could break the clients into more pieces. One piece to handle data requests and another piece to generate the requests.
Based on your description that the clients make this data request "so often", I think you should try the first method. If your clients are requesting data frequently enough, having the server collect and cache the client information will actually result in fresher data for the clients.
I have two instances of cowboy server running which are connected to RabbitMQ. I am using gen_bunny as RabbitMQ client to connect to RabbitMQ.
I can consume the message to from rabbitMQ if using bunnyc:consume(). However for that I need to fire this method explicitly. What I want is to bind an event on cowboy so as soon as there is a message in the Queue it should automatically notify to cowboy.
Is it possible using gen_bunny or other erlang client?
Dont know about gen_bunny, but with official erlang client you can subscribing to queue (look at http://www.rabbitmq.com/erlang-client-user-guide.html, "Subscribing To Queues" section)
As far as i understand, you need send messages from queue through WebSockets to clients. So you need subscribe to queue in process that communicate with client. And recieve messages in "receive ... end" or in handle_info (depends on realization)
ADDITION
I looked in gen_bunny sources... mochi/gen_bunny depends on mochi/amqp_client which provide amqp_channel:subscribe/3 (see https://github.com/mochi/amqp_client/blob/master/src/amqp_channel.erl#L177) you can use it for subscribing
Got it worked ... After some tweaking in the bunnyc.erl source. Now, In init function i have added subscription function and in start_link function in bunnyc.erl passing the process id of my cowboy process so as soon as there is a message in the queue I can get it in websocket_info function of cowboy..
In a concept proof I'm developing, I've built the folowing scheme:
_ A _ _
/ | \ \
SS S H CC
/ \
C C
In which:
A - Application
SS - Supervisor
CC - "Real" client
S - gen_server
H - gen_event
C - "internal" client
The application works like a multiplexer. I connect on my internal client on my server and request some stuff. Then I pass this request to my real client (witch connects to the real application) and distribute it over any internal client who is demand that kind of stuff.
Everything is working fine except for the event handling: I wanted to make my internal client send messages directly to the gen_event handlers, and he would intermediate the process of requesting to the real server/ reading from cache and replying to the client.
I figured that I need to make a link between the event handler and my client handling process, this is right? Maybe it's the case to make the event handler to be a global process and just pump messages from between clients?
Sorry for the long question.
I am not sure I understand the question but assuming you are after a sort of message switch to enable Clients to exchange messages in a distributed fashion, you could look at one my projects that does just that: Mswitch.
Of course if you register a "global process" (accessible locally and/or remotely), you can always shuttle messages to it and have this process distribute the said messages as you see fit. Be sure not to forget about the common pitfalls of making processes in different Emulator instances talk to each other (cookie sync and registered VM name).
If you register the handler, it will have a globally known name , and then you can send messages to it directly.