Messages received from port in erlang-sqlite3 - erlang

Erlang-sqlite3 uses a port driver to connect with the SQLite database, and receives messages from the port:
wait_result(Port) ->
receive
{Port, Reply} ->
% io:format("Reply: ~p~n", [Reply]),
Reply;
{error, Reason} ->
io:format("Error: ~p~n", [Reason]),
{error, Reason};
_Else ->
io:format("Else: ~p~n", [_Else]),
_Else
end.
I thought that messages from ports should look like this:
{Port,{data,Data}} Data is received from the external program.
{Port,closed} Reply to Port ! {Pid,close}.
{Port,connected} Reply to Port ! {Pid,{connect,NewPid}}
{'EXIT',Port,Reason} If the port has terminated for some reason.
So, when uncommenting the io:format line in {Port, Reply} clause, I should expect to see {data, ...} for actual replies. I don't; instead I see (for test.erl)
Reply: {ok,101}
Reply: [{columns,["name"]},{rows,[{<<"user">>}]}]
Reply: [{columns,["sql"]},
{rows,[{<<"CREATE TABLE user (id INTEGER PRIMARY KEY, name TEXT, age INTEGER, wage INTEGER)">>}]}]
Reply: {id,1}
Reply: {id,2}
Reply: [{columns,["id","name","age","wage"]},
{rows,[{1,<<"abby">>,20,2000},{2,<<"marge">>,30,2000}]}]
Reply: [{columns,["id","name","age","wage"]},{rows,[{1,<<"abby">>,20,2000}]}]
Reply: [{columns,["id","name","age","wage"]},
{rows,[{1,<<"abby">>,20,2000},{2,<<"marge">>,30,2000}]}]
Reply: {ok,101}
Reply: [{columns,["id","name","age","wage"]},{rows,[{1,<<"abby">>,20,2000}]}]
Reply: {ok,101}
Where am I going wrong?
Will messages I get on a port error look like {'EXIT',Port,Reason} or not?

It seems that between your process and port is another process involved which decodes real port messages. Are you sure that Port is really Port?. Try io:format("Port: ~p~n", [Port]) If you will see something like #Port<0.500> it is port, if it will be something like <0.38.0> there is the man in the middle.

The relevant example in http://www.erlang.org/doc/apps/erts/driver.html is the last one. It turns out that when using driver_output_term, the term is sent by itself:
receive
Result ->
Result
end.
instead of
receive
{Port, {data, Result}} ->
Result
end.

Related

Erlang: difference between registering process and assigning Pid to variable

Does it make a difference if I register a newly spawned process using register(atom, spawn..) or if I do Pid = spawn..?
To take an example, I just did this with an old program from the Programming Erlang book:
Let's first make a simple server loop:
-module(geometry_server).
-export([loop/0]).
loop() ->
receive
{Client, {square, S} = Tuple} ->
io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
Client ! {self(), Tuple, S*S},
loop()
end.
Now a client:
-module(geometry_client).
-export([client/2, start_server/0]).
client(Pid_server, Geom_tuple) ->
Pid_server ! {self(), Geom_tuple},
receive
{Pid_server, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and server was ~p~n", [Geom_tuple, Area, Pid_server])
after 1000 ->
io:format("~p~n",["received nothing from server"] )
end.
start_server() -> spawn(geometry_server, loop, []).
After compiling both, I do
register(q, Q = geometry_client:start_server()).
Then I call them and get results as follows:
5> geometry_client:client(Q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
Client: Area of {square,2} is 4 and server was <0.77.0>
ok
6> geometry_client:client(q, {square,2}).
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
"received nothing from server"
ok
Why does the client not receive anything from the server when I use the registered atom?? The server obviously received the message from the client.
I can confirm that the server sent a message, because after the above if I do
7> geometry_client:client(whereis(q), {square,2}).
Client: Area of {square,2} is 4 and server was <0.77.0>
Server: Area of square of Side 2 is 4 and Client was <0.60.0>
ok
12>
So I conclude that the mailbox already has the message from the server from the previous command, which is why the Client output gets printed before the Server message has been received and printed...
What am I missing?? Why is there a problem receiving the message when I use the registered atom?
The receive in your client/2 function waits for a message matching {Pid_server, Geom_tuple, Area}. When you pass q as the argument, Pid_server is q, but the message the server sends back to the client is a tuple with the first element always being the actual PID of the server, not its name, which means your receive ends up in the after block.
There are many ways to solve this. You can modify client/2 to use whereis/1 to get the PID of the registered process and use that in receive if Pid_server is an atom.
The best way would be to use references here (see make_ref/0). You create a reference when sending a message to the server, and the server sends it back in the response. This way you're guaranteed that you're receiving the response for the request you just sent, since every reference returned by make_ref/0 is guaranteed to be unique.
In client/2, do:
client(Pid_server, Geom_tuple) ->
Ref = make_ref(),
Pid_server ! {Ref, self(), Geom_tuple},
receive
{Ref, Geom_tuple, Area} -> io:format("Client: Area of ~p is ~p and server was ~p~n", [Geom_tuple, Area, Pid_server])
after 1000 ->
io:format("~p~n",["received nothing from server"] )
end.
And in the server:
loop() ->
receive
{Ref, Client, {square, S} = Tuple} ->
io:format("Server: Area of square of Side ~p is ~p and Client was ~p~n", [S, S*S, Client]),
Client ! {Ref, Tuple, S*S},
loop()
end.

Erlang: gen_tcp:recv() does not receive packet sent from client?

I modified this server to use gen_tcp:recv inorder to limit the number of bytes for a packet to 50. I commented out the line inet:setopts(Socket, [{active, once}]), because gen_tcp:recv is supposed to be {active,false}. This is the client side erl shell
2> cp3:client().
exit
3>
and this is the server side erl shell
4> cp3:server().
Started Server:
<0.46.0>
Accept Server:
Pid <0.48.0>
Connection accepted
Accept Server:
Loop Server:
5>
I also wondered how can I know if the socket closed with the return value {tcp_closed, Socket} if gen_tcp:recv doesn't create one?
-module(cp3).
-export([client/0, server/0,start/0,accept/1,enter_loop/1,loop/1]).
client() ->
{ok, Socket} = gen_tcp:connect("localhost", 4001,[list, {packet, 0}]),
ok = gen_tcp:send(Socket, "packet"),
receive
{tcp,Socket,String} ->
io:format("Client received = ~p~n",[String]),
io:format("Client result = ~p~n",[String]),
gen_tcp:close(Socket)
after 1000 ->
exit
end.
server() ->
Pid = spawn(fun()-> start() end),
Pid.
start() ->
io:format("Started Server:~n"),
{ok, Socket} = gen_tcp:listen(4001, [binary, {packet, 0},{reuseaddr, true},{active, false}]),
accept(Socket).
accept(ListenSocket) ->
io:format("Accept Server:~n"),
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
Pid = spawn(fun() ->
io:format("Connection accepted ~n", []),
enter_loop(Socket)
end),
io:format("Pid ~p~n",[Pid]),
gen_tcp:controlling_process(Socket, Pid),
Pid ! ack,
accept(ListenSocket);
Error ->
exit(Error)
end.
enter_loop(Socket) ->
%% make sure to acknowledge owner rights transmission finished
receive ack -> ok end,
loop(Socket).
loop(Socket) ->
%% set socket options to receive messages directly into itself
%%inet:setopts(Socket, [{active, once}]),
io:format("Loop Server:~n"),
case gen_tcp:recv(Socket, 50) of
{ok, Data} ->
case Data of
<<"packet">> ->
io:format("Server replying = ~p~n",[Data]),
gen_tcp:send(Socket, Data),
loop(Socket)
end;
{error, Reason} ->
io:format("Error on socket ~p reason: ~p~n", [Socket, Reason])
end.
I am not very clear about your question, but the above code does not work. Hope the following answers your problem. Your tcp receive case gen_tcp:recv(Socket, 50) of has one error. It is waiting for 50 bytes to read. Check the documentation of gen_tcp:recv/2. Change the length (length of packer 6 but preferably to) 0 to receive all the bytes.
The value does not limit the size of data, but it will not send the data back until it receives data of length 50. Instead you may need to accept and then check it.

Erlang gen_udp sending packets to ip address?

I tried using the following code for a simple test to send packets across the Internet. I did check to see if the localhost version works with the following commands and it did work but it doesn't work if I replace localhost (127.0.0.1) with a real internet address, mine. I just get 0 on the client side and nothing changes with the server side, although using localhost is the same with the server side.
Server side:
udp_test:start_server().
Client side:
udp_test:client(40).
Simple enough but when I replace 127.0.0.1 with my ip address I get nothing ( I turned off the firewall too). The basic topology of my home network consists of a wireless modem (using dsl) and my computer is connected via a wireless usb card. There are about two other active computers on the network. I thought that may have something to do with the connection.
I also wondered how could I modify the code so I can type udp_test:client(40,127.0.0.1).
instead and change the client function to accept two arguments in udp_test? I tried simply doing client(N,ip) and changing the function gen_udp:send(Socket, "ip",,) but that came up with a no matching clause error.
-module(udp_test).
-export([start_server/0, client/1]).
start_server() ->
spawn(fun() -> server(4000) end).
%% The server
server(Port) ->
{ok, Socket} = gen_udp:open(Port, [binary]),
io:format("server opened socket:~p~n",[Socket]),
loop(Socket).
loop(Socket) ->
receive
{udp, Socket, Host, Port, Bin} = Msg ->
io:format("server received:~p~n",[Msg]),
N = binary_to_term(Bin),
Fac = fac(N),
gen_udp:send(Socket, Host, Port, term_to_binary(Fac)),
loop(Socket)
end.
fac(0) -> 1;
fac(N) -> N * fac(N-1).
%% The client
client(N) ->
{ok, Socket} = gen_udp:open(0, [binary]),
io:format("client opened socket=~p~n",[Socket]),
ok = gen_udp:send(Socket, "127.0.0.1", 4000,
term_to_binary(N)),
Value = receive
{udp, Socket, _, _, Bin} = Msg ->
io:format("client received:~p~n",[Msg]),
binary_to_term(Bin)
after 2000 ->
0
end,
gen_udp:close(Socket),
Value.
write {127,0,0,1} instead of "127.0.0.1".
Ip address

problems while sending messages using ssl:send

I am writing some code which sends data over ssl sockets.
The sending part is inside a gen_server:call/3 as:
handle_call({send, Data}, _From, #state{socket=Socket} = State) ->
Reply = case ssl:send(Socket, Data) of
ok ->
ok;
{error, Error} ->
{error, Error}
end,
{reply, Reply, State}.
the problem is that if i kill the application which behaves as server at the other side of the connection, the result of the call is 'ok' but the Data is not sent. Does that mean that the socket is viewed as alive untile {ssl_closed, S} is received by the process?
It was my mistake, data is actually sent but never recovered by peer.

how to ensure message sending with ssl

I am implementing as exercise a gen_server which behaves as interface towards an ssl authentication server. The ssl server severes the connection if a packet received is wrong (e.g. wrong username and password). The connection must be persistent.
In my gen_server, I open the ssl connection towards the server with an handle_cast/2:
handle_cast(connect, State) ->
......
case ssl:connect(Address, Port, Options, Timeout) of
{ok, NewSocket} ->
{noreply, State#state{socket=NewSocket}};
{error, Reason} ->
gen_server:cast(?SERVER, connect),
{noreply, State#state{socket=undefined}};
and then I wait for other other messages in handle_cast/2 which can be sent for example using:
gen_server:cast(Pid, {authenticate, User, Password}).
Whenever I receive such a cast message I spawn a new function which recovers the SSL socket from the server state using a gen_server:call/3 and sends the authentication message to the SSL server. If the sending part returns an error I try to reconnect, otherwise I read for a while on the socket, to be sure that the socket does not go down, and if it does I reconnect.
send_auth(_, _, 0) ->
{error, max_num_reached};
send_auth(User, Password, Num) ->
Socket = gen_server:call(?SERVER, socket),
%% also a check that socket is not 'undefined'
case ssl:send(Socket, AuthMessage) of
ok ->
case ssl:recv(Socket, 0, 2000) of
{error, timeout} ->
ok;
_ ->
gen_server:cast(?SERVER, connect),
send_auth(User, Password, Num-1)
end,
{error, closed} ->
gen_server:cast(?SERVER, connect),
send_auth(User, Password, Num-1)
end.
I made many tests, but every time, if one message (not the last) is wrong, none of the following messages is actually delivered.
How can I grant that all valid authentication messages are delivered to the authentication server? Moreover how can be sure that the server will connect only if it is not already trying to do so? Otherwise that would be like DOS attack!
As I can understand there's only one authentication server in the system so why can't you connect to it right in the gen_server's init? If the connection is somewhat reliable, you can just kill gen_server if there are any issues with connection and let supervisor re-launch it. It probably will solve your DOS concern.
When you spawn additional processes with send_auth you can just pass socket connection in arguments, instead of doing gen_server call.
To guarantee the delivery you'll add some kind of acknowledgement into your protocol. Auth server should reply to send_auth with something confirming that it got it. And send_auth should retry until it receives that acknowledgement or have some other fallback behavior in case it never does.

Resources