I am making an example when using gen_sctp create soscket and associates in server-client model.
In server side:
{ok,serverSocket} = gen_sctp:open(1234,[{ip,{127,0,0,1}},{reuseaddr,true},{active,true}]).
ok = gen_sctp:listen(S,true).
In client side:
{ok,Client} = gen_sctp:open(1243,[{ip,{127,0,0,1}},{reuseaddr,true}]).
{ok,Ass} = gen_sctp:connect(S,{127,0,0,1},1234,[{active,true}]).
And then client send message to server by send/4:
gen_sctp:send(S,Ass,2,<<"hellooooo">>).
And message receive in server side:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
So how can server can reply message to client by send/3?
Thanks and Best Regards,
Tran.
gen_sctp:send/3 is like gen_sctp:send/4 but you can set more flags and options. You have already used gen_sctp:send/4 in client code (while you messed around with client and server sockets):
{ok, Assoc} = gen_sctp:connect(ClientSocket, {127,0,0,1}, 1234,[{active,true}]).
gen_sctp:send(ClientSocket, Assoc, 2, <<"hellooooo">>).
And Assoc is of sctp_assoc_change record type while gen_sctp:send/4 looks just for assoc_id if you provide sctp_assoc_change. So currently providing #sctp_assoc_change{} or just association id behave exactly same.
And how one can find out association id of client in server? It's provided in message which server received:
{sctp,#Port<0.6126>,
{127,0,0,1},
1243,
{[{sctp_sndrcvinfo,2,1,[],0,0,0,1409953138,0,18}],
<<"hellooooo">>}}
#sctp_sdnrcvinfo{} record has fields telling association id and stream number which data is received from. You can get current association id from assoc_id field and pass it to another gen_sctp:send/4:
gen_sctp:send(ServerSocket, AssocID, 2, <<"welcome!">>).
Stream number of 2 probably won't make it fail because by default gen_sctp:open makes 10 incoming and outgoing streams, but you can safely provide 0 as stream number.
Here is an example of sending and receiving data with sctp:
#!/usr/bin/escript
-include_lib("kernel/include/inet_sctp.hrl").
server_loop(Socket) ->
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{assoc_id=AssocID}],
Payload}} ->
gen_sctp:send(Socket, #sctp_sndrcvinfo{assoc_id=AssocID, stream=0},
<<"pong">>),
% or less complex gen_sctp:send/4
gen_sctp:send(Socket, AssocID, 0, <<"pong">>);
Rest ->
io:format("server got unhandled message ~w~n", [Rest])
end,
server_loop(Socket).
create_server_socket() ->
{ok, Socket} = gen_sctp:open(1234, [{ip,{127,0,0,1}}, {reuseaddr,true},
{active,true}]),
gen_sctp:listen(Socket, true),
{ok, Socket}.
run_server() ->
Spawner = self(),
spawn_link(fun() ->
{ok, Socket} = create_server_socket(), Spawner ! ready, server_loop(Socket)
end),
receive
ready ->
io:format("server is up~n"),
ok
after 100 ->
throw(server_timeout)
end.
ping_server() ->
{ok, Socket} = gen_sctp:open(1243, [{ip,{127,0,0,1}}, {reuseaddr, true}]),
{ok, AssocChange} = gen_sctp:connect(Socket, {127,0,0,1}, 1234, [{active, true}]),
gen_sctp:send(Socket, AssocChange, 2, <<"ping">>),
receive
{sctp, Socket, _FromIP, _FromPort, {[#sctp_sndrcvinfo{}], Payload}} ->
io:format("client got payload ~p~n", [Payload])
after 2000 ->
throw(client_timeout)
end.
main([]) ->
run_server(),
ping_server().
Related
Writing a very simple TCP server for starters, but for the life of me, gen_tcp:accept in my 'accept_loop' function keeps returning {error, closed} immediately when it's called. So far I've been able to get everything else working thus far, but this one is really stumped me. Wondering if I fell into a beginner trap.
Anyway here is the code.
-module(doom_server).
-behaviour(gen_server).
-export([start_link/1, handle_client/1, accept_loop/1, accept/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-define(TCP_OPTIONS, ).
-record(server_state, {
port,
ip=any,
timeout=10,
lsocket=null}).
start_link(_Args) ->
gen_server:start_link(?MODULE, _Args, []).
init(_Args) ->
% Get configuration from doom.config and create a new server state.
Port = proplists:get_value(port, _Args),
BindAddress = proplists:get_value(bind_address, _Args),
Timeout = proplists:get_value(timeout, _Args),
case gen_tcp:listen(Port, [binary,
{packet, 0},
{active, false},
{reuseaddr, true}]) of
{ok, ListenSocket} ->
% Construct our server state.
NewState = #server_state {
lsocket = ListenSocket,
port = Port,
ip=BindAddress,
timeout=Timeout
},
{ok, accept(NewState)};
{error, Reason} ->
io:format("Failed: ~w~n", [Reason]),
{stop, Reason}
end.
handle_cast({accepted, _Pid}, State=#server_state{}) ->
{noreply, accept(State)}.
accept_loop({Server, ListenSocket, Timeout}) ->
case gen_tcp:accept(ListenSocket, Timeout) of
{ok, Socket} ->
% Let the server spawn a new process and replace this loop
gen_server:cast(Server, {accepted, self()}),
handle_client(Socket);
{error, Type} ->
io:format("Error: ~w~n", [Type])
end.
% To be more robust we should be using spawn_link and trapping exits
accept(State = #server_state{lsocket=ListenSocket, timeout=Timeout}) ->
proc_lib:spawn(?MODULE, accept_loop, [{self(), ListenSocket, Timeout}]),
State.
handle_client(Socket)->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
gen_tcp:send(Socket, Data),
handle_client(Socket);
{error, closed} ->
ok
end.
% We get compile warnings from gen_server unless we define these
handle_call(_Msg, _Caller, State) -> {noreply, State}.
handle_info(_Message, Library) -> {noreply, Library}.
terminate(_Reason, _Library) -> ok.
code_change(_OldVersion, Library, _Extra) -> {ok, Library}.
I've ran erlang:port_info(ListenSocket) just after the gen_tcp:listen and inside the accept_loop. Both output the same data, and if I put gen_tcp:accept inside the main thread or right after the gen_tcp:listen it works as expected. Am I doing something wrong when I spawn a new process?
P.S I'm using this tutorial as point of reference: http://20bits.com/article/erlang-a-generalized-tcp-server
Is there a way to query for user presence in XMPP, given that the user's subscription type is 'both'?
Since i am building for mobile platform, i have blocked all incoming presence stanzas using privacy list. In my use case, a user would be at least be subscribed to 500 users and processing these many presence stanzas would put a lot of stress on the mobile device.
So instead of processing all the user stanzas, i would like to get the presence for a user only when i query for it.
There is no such feature at the moment inside ejabberd, but that's definitely something you can develop as a plugin. You can write a plugin that will be handling http requests using HTTP webserver and do whatever processing and security check you want before answering with the user presence.
For future reference, i have managed to pull together some code(thanks to mod_last.erl) and build a module that lets you query for user presence. Suggestions & feedbacks will be highly appreciated.
-module(mod_query_presence).
-behaviour(gen_mod).
-export([start/2, stop/1,
process_sm_iq/3
]).
-include("ejabberd.hrl").
-include("jlib.hrl").
-include("logger.hrl").
-include("mod_privacy.hrl").
-define(NS_QUERY_PRESENCE, <<"jabber:iq:qpresence">>).
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
?NS_QUERY_PRESENCE, ?MODULE, process_sm_iq, IQDisc),
?INFO_MSG("Loading module 'mod_iqtest' v.01", []).
stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_QUERY_PRESENCE),
?INFO_MSG("Stoping module 'mod_iqtest' ", []).
process_sm_iq(From, To,
#iq{type = Type, sub_el = SubEl} = IQ) ->
case Type of
set ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
get ->
User = To#jid.luser,
Server = To#jid.lserver,
Resource = xml:get_tag_attr_s(list_to_binary("resource"), SubEl),
{Subscription, _Groups} =
ejabberd_hooks:run_fold(roster_get_jid_info, Server,
{none, []}, [User, Server, From]),
if (Subscription == both) or (Subscription == from) or
(From#jid.luser == To#jid.luser) and
(From#jid.lserver == To#jid.lserver) ->
UserListRecord =
ejabberd_hooks:run_fold(privacy_get_user_list, Server,
#userlist{}, [User, Server]),
case ejabberd_hooks:run_fold(privacy_check_packet,
Server, allow,
[User, Server, UserListRecord,
{To, From,
#xmlel{name = <<"presence">>,
attrs = [],
children = []}},
out])
of
allow -> get_presence(IQ, SubEl, User, Server, Resource);
deny ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end;
true ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_FORBIDDEN]}
end
end.
get_presence(IQ, SubEl, LUser, LServer, LResource) ->
case ejabberd_sm:get_session_pid(LUser, LServer, LResource) of
none ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
Pid ->
{_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs =
[{<<"xmlns">>, ?NS_QUERY_PRESENCE},
{<<"status">>, Status},
{<<"StatusText">>, StatusText}],
children = []}]}
end.
IQ request format
<iq id='id' to='56876c654366178e0e75a8cd#192.168.1.150' type='get'>
<query xmlns='jabber:iq:qpresence' resource='Smack'/>
</iq>
IQ reply format if user is online
<iq from='56876c654366178e0e75a8cd#192.168.1.150' to='56876c654366178e0e75a8cd#192.168.1.150/Smack' id='last1' type='result'>
<query xmlns='jabber:iq:qpresence' status='dnd' StatusText='YO'/>
</iq>
If the user is not online, you will get an service-unavailable error.
I Wrote this module for a simple server program that receives a request and send a response to client. It works fine But when if recompile this module while a client connected, the client disconnects and if i reconnect client to this server and send a request, there is no response. Is there any idea?!
Here is my module:
-module(controller).
-export([start/1, loop/1, response/2]).
-include_lib("types.hrl").
-define(END_CHAR, "$").
-spec start(Port) -> no_return() when
Port :: char().
-spec loop(Listen) -> no_return() when
Listen :: port().
-spec handler(Socket) -> no_return() when
Socket :: port().
-spec response(HandlerPID, Data) -> {send_msg, Msg} when
HandlerPID :: pid(),
Data :: string(),
Msg :: response().
start(Port) ->
{ok, Listen} = gen_tcp:listen(Port, [{active, once}]),
spawn(?MODULE, loop, [Listen]).
loop(Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
spawn(?MODULE, loop, [Listen]),
handler(Socket).
handler(Socket) ->
receive
{tcp, Socket, Data} ->
%io:format("recv ~p~n", [Data]),
spawn(?MODULE, response, [self(), Data]),
inet:setopts(Socket, [{active, once}]),
handler(Socket);
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
{send_msg, Msg} ->
gen_tcp:send(Socket, lists:flatten(io_lib:format("~p", [Msg])) ++ ?END_CHAR),
handler(Socket)
end.
response(PID, Data) ->
[Req|Args] = string:tokens(Data, ?END_CHAR),
{ReqPID, ReqRef} = spawn_monitor(view, request, [list_to_atom(Req), self(), Args]),
receive
{'DOWN', ReqRef, process, ReqPID, {function_clause, _}} -> PID ! {send_msg, invalid_request};
{'DOWN', ReqRef, process, ReqPID, {{case_clause, _}, _}} -> PID ! {send_msg, bad_args};
{'DOWN', ReqRef, process, ReqPID, {{badmatch, _}, _}} -> PID ! {send_msg, bad_args};
Resp -> PID ! {send_msg, Resp}
end.
Note
{tcp_closed, Socket} ->
%io:format("disconnected~n", []),
gen_tcp:close(Socket);
in handler. It doesn't call handler again, it just returns and so the server process stops.
Your loop function name is also misleading, handler is the actual loop.
I would recommend to take a look at Ranch - A socket acceptor pool for the TCP connections.
i use ranch to listen socket, but in a short time about five seconds, ranch closed the socket, and my setting of socket is above, so what' wrong?
{ok, _} = ranch:start_listener(server,200, ranch_tcp, [{port, 5555},{active, once}, {max_connections, 1024}], server_protocol, []), %% start the listener
the protocol file is below, the ranch listen to accept a socket, and the reverse the receive data, but what's wrong is that, when send data back to the client, after about five seconds, the client receive the message says that the socket is closed by server, i don't know if is ranch's default settings cause this?
-module(reverse_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
%% API.
-export([start_link/4]).
%% gen_server.
-export([init/1]).
-export([init/4]).
-export([handle_call/3]).
-export([handle_cast/2]).
-export([handle_info/2]).
-export([terminate/2]).
-export([code_change/3]).
-define(TIMEOUT, 5000).
-record(state, {socket, transport}).
%% API.
start_link(Ref, Socket, Transport, Opts) ->
proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
%% gen_server.
%% This function is never called. We only define it so that
%% we can use the -behaviour(gen_server) attribute.
init([]) -> {ok, undefined}.
init(Ref, Socket, Transport, _Opts = []) ->
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}]),
gen_server:enter_loop(?MODULE, [],
#state{socket=Socket, transport=Transport},
?TIMEOUT).
handle_info({tcp, Socket, Data}, State=#state{
socket=Socket, transport=Transport}) ->
Transport:setopts(Socket, [{active, once}]),
Transport:send(Socket, reverse_binary(Data)),
{noreply, State, ?TIMEOUT};
handle_info({tcp_closed, _Socket}, State) ->
{stop, normal, State};
handle_info({tcp_error, _, Reason}, State) ->
{stop, Reason, State};
handle_info(timeout, State) ->
{stop, normal, State};
handle_info(_Info, State) ->
{stop, normal, State}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% Internal.
reverse_binary(B) when is_binary(B) ->
[list_to_binary(lists:reverse(binary_to_list(
binary:part(B, {0, byte_size(B)-2})
))), "\r\n"].
So your problem is because your gen_server process is timing out and shutting down. The socket being closed is a side effect of this because ranch links the Socket to the spawned handler process.
Once the new process enters the gen_server loop with the call to gen_server:enter_loop, it has ?TIMEOUT milliseconds to receive a message before it is sent a timeout message.
-define(TIMEOUT, 5000).
init(Ref, Socket, Transport, _Opts = []) ->
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}]),
gen_server:enter_loop(?MODULE, [],
#state{socket=Socket, transport=Transport},
?TIMEOUT). %% timeout because of this!
handle_info({tcp, Socket, Data}, State=#state{
socket=Socket, transport=Transport}) ->
Transport:setopts(Socket, [{active, once}]),
Transport:send(Socket, reverse_binary(Data)),
{noreply, State, ?TIMEOUT}; %% timeout because of this!
So when those five second pass and the gen_server hasn't received any messages in that time it sends itself a timeout message, which is then handled by handle_info
handle_info(timeout, State) ->
{stop, normal, State};
Your handle_info tells the gen_server to stop, which causes the Socket to close because the two are linked together.
You can either remove the timeouts completely, or just stop the timeout from causing the process to close.
Here is how I would change the handle_info timeout code:
handle_info(timeout, State) ->
io:format("the socket is idle~n"),
{noreply,State};
I'm a erlang newbie.
When I read the Socket chapter from "Programming Erlang" and doing some examples according to the tutorial, there is a problem.
In the chapter "The Hybrid Approach (Partial Blocking)", I do the following:
loop(Socket)->
receive
{tcp, Socket, Bin} ->
io:format("Server received binary = ~p~n",[Bin]),
Str = binary_to_term(Bin),
io:format("Server (unpacked) ~p~n",[Str]),
Reply = lib_misc:string2value(Str),
io:format("Server replying = ~p~n",[Reply]),
gen_tcp:send(Socket, term_to_binary(Reply)),
inet:setopts(Socket,[binary,{active, once}]), %Configure socket as active
loop(Socket);
{tcp_closed, Socket} ->
io:format("Server socket closed~n")
end.
start()->
{ok,Listen} = gen_tcp:listen(2345,[binary,{packet,4},
{reuseaddr, true},
{active, once}]),
{ok, Socket} = gen_tcp:accept(Listen),
loop(Socket).
sendData(Str)->
{ok,Socket}=gen_tcp:connect("localhost",2345,[binary,{packet,4}]),
ok=gen_tcp:send(Socket, term_to_binary(Str)),
receive
{tcp,Socket,Bin}->
io:format("Client received binary = ~p~n",[Bin]),
Val = binary_to_term(Bin),
io:format("Client result=~p~n",[Val])
% gen_tcp:close(Socket)
end.
Then I open one terminal like this:
$erl
server:start().
and open another terminal like this:
$erl
client:sendData("1233").
At server, it will output:
Server received binary = <<131,107,0,3,49,50,51>>
Server (unpacked) "123"
Server replying = 123
At client, it will output:
Client received binary = <<131,97,123>>
Client result=123
But when the client sendData second time, there is not any response at server.
Is there something wrong? Thank you.
You server listen only one connection. You start function should be in another loop too.