Erlang UDP packet not received - erlang

I'm trying to get a client (behind a NAT) to send packet to a dedicated server.
Here's my code :
-module(udp_test).
-export([start_client/3, listen/1, send/4, start_listen/1]).
start_client(Host, Port, Packet) ->
{ok, Socket} = gen_udp:open(0, [{active, true}, binary]),
io:format("client opened socket=~p~n",[Socket]),
spawn(?MODULE, send, [Socket, Host, Port, Packet]).
start_listen(Port) ->
{ok, Socket} = gen_udp:open(Port, [binary]),
spawn(?MODULE, listen, [Socket]).
listen(Socket) ->
inet:setopts(Socket, [{active, once}]),
receive
{udp, Socket , Host, Port, Bin} ->
gen_udp:send(Socket, Host, Port, "Got Message"),
io:format("server received:~p / ~p~n",[Socket, Bin]),
listen(Socket)
end.
send(Socket, Host, Port, Packet) ->
timer:send_after(1000, tryToSend),
receive
tryToSend ->
io:fwrite("Sending: ~p / to ~p / P: ~p~n", [Packet, Host, Port]),
Val = gen_udp:send(Socket, Host, Port, Packet),
io:fwrite("Value: ~p~n", [Val]),
send(Socket, Host, Port, Packet);
_ ->
io:fwrite("???~n")
end.
on the dedicated server I launch the listen function :
# erl -pa ebin
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:4:4] [async-threads:0] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> udp_test:listen(4000).
on the client side I launch the sending loop :
$ erl -pa ebin
Erlang R15B (erts-5.9) [source] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9 (abort with ^G)
1> udp_test:start_client("ip.of.my.server", 4000, "HELLO !!!").
client opened socket=#Port<0.737>
<0.33.0>
Sending: "HELLO !!!" / to "ip.of.my.server" / P: 4000
Value: ok
Sending: "HELLO !!!" / to "ip.of.my.server" / P: 4000
Value: ok
Sending: "HELLO !!!" / to "ip.of.my.server" / P: 4000
Value: ok
Although gen_udp:send from the client returns ok, the server doesn't seems to receive any of these packets, as it should print "server received: "HELLO !!!"" on the console.
anyone would have an idea why this is not working.
EDIT 1 :
There is no firewall or iptable configured on the dedicated server.
The connection works fine through TCP between the client and the server, but not UDP.
When I try to run both server and client on the same device (different erlang node), it does not work either.
EDIT 2 :
changed the code for the listen part looping on itself re-creating the Socket each time a message is received... but still does not work.

Your start_client/3, send/4 loop should work as expected, although it is a slightly convoluted way to get a 1 sec delay.
Your listen/1 will not do as expected and should at most return one message:
For each loop it opens a new socket, the first time it uses the port to open the socket while in the following loops it uses the socket to open a new socket, which should generate an error.
You set the socket to {active,once} so you will at most receive one packet on the socket before you reset it to being active once.
Why try on the receiving side doing something simple like:
Erlang R15B (erts-5.9) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9 (abort with ^G)
1> {ok,S}=gen_udp:open(5555,[]).
{ok,#Port<0.582>}
2> flush().
Shell got {udp,#Port<0.582>,{127,0,0,1},4444,"hej"}
Shell got {udp,#Port<0.582>,{127,0,0,1},4444,"there"}
ok
3>
as a first step to test the basic connection?
EDIT:
In your new version you call start_listen/1 which opens a UDP socket and then spawns a process to sit and listen on it. The process which opens the port is the ports controlling process. The messages sent when a packet arrives is sent to the controlling process, which in this case is not the controlling process.
There are two ways to fix this:
Spawn a process which first opens the port and and then goes into a loop receiving the UDP messages from the socket. This is how it is done in the example you referenced: start spawns a process running server which opens the port and then calls loop.
Use gen_udp:controlling_process/2 to pass control of the socket to the loop process so it will receive the packet messages.
Both work and have their place. You had the same structure in the original code but I missed it.

Found the issue, I was starting the socket, then only spawning the loop...
Either change the controlling_process to the spawned pid, or open the socket on the spawned pid.
Hope it helps someone.

Related

Erlang - use of lists when the size is consistently changing over time

I am new to Erlang and I am learning by building a really small chat program in pure Erlang.
I would like for clients to be able to connect to a server and then send messages to each other. But this is all done on a local machine and not over a network just for learning.
I have a list of all the clients that have connected to the server.
If client A sends a message to client B, I get the desired output in client A's terminal but I can't work out how to get the message from client A to be displayed on client B's terminal.
Or do I have to set each client with it's own mini server
-module(server).
-export([start/0]).
-export([server/1]).
-export([connect/0]).
-export([sendMessage/2]).
%%
%% The Server
%%
start() ->
EmptyList = [],
Pid = spawn(server, server, [EmptyList]),
register(chatServe, Pid).
server(ListOfClients) ->
receive
{Client, connect} ->
Client ! {chatServe, connected},
List = clientList(ListOfClients, Client),
server(List);
{Client, message, MessageBody} ->
List = ListOfClients,
lists:foreach(fun(X) -> X ! {chatServe, new_message, MessageBody} end, List),
Client ! {chatServe, received},
server(List)
end.
%%
%% The client will call rpc:call(server#local, server, connect, [])
%% to connect
%%
connect() ->
chatServe ! {self(), connect},
receive
{chatServe, connected} -> connected
end.
%%
%% The send message method takes two args
%%
%%
sendMessage(SendTo, MessageBody) ->
chatServe ! {self(), message, MessageBody},
receive
{chatServe, received} -> received
end.
receiveMessage(SendTo, SendFrom, MessageBody) ->
receive
{}
end.
%%
%% Some helper functions
%%
clientList(List, Client) when length(List) =:= 0 ->
io:format("List Size = 1~n"),
[Client];
clientList(List, Client) ->
io:format("List size = ~p~n", [length(List) + 1]),
[Client | List].
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) when H =:= SendTo ->
SendTo ! {SentFrom, message, MessageBody};
forwardMessage(SendTo, SentFrom, MessageBody, [H | T]) ->
forwardMessage(SendTo, SentFrom, MessageBody, T);
forwardMessage(SendTo, SentFrom, MessageBody, []) -> [].
In the terminal of the client I am calling
rpc:call(host, mod, function, args).
So my question is how can I get Client A to send a message to Client B through server C, with Client A displaying success, and Client B displaying the message that was sent?
Thanks in advance
In this example my server is an Erlang node and every client is an erlang node too.
Code:
-module(test).
-export([server_start/1, client_start/2]).
-export([server_new_message/2, client_new_message/2]).
server_start(ServerName) ->
{ok, _Pid} = net_kernel:start([ServerName, shortnames]),
erlang:register(server, erlang:self()),
io:format("Server '~p' started.~nMessages: ~n ~n", [erlang:node()]),
server_loop().
server_loop() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
Receivers = lists:delete(Name, erlang:nodes()),
rpc:multicall(Receivers, ?MODULE, client_new_message, [Name, Text]),
server_loop()
end.
%% Server runs this function in client's node.
%% 'client' process in client's node will receive this message and print it
client_new_message(Name, Text) ->
client ! {msg, Name, Text}.
client_start(ServerName, ClientName) ->
{ok, _Pid} = net_kernel:start([ClientName, shortnames]),
pong = net_adm:ping(ServerName),
timer:sleep(1000), % wait for updating erlang:nodes()
Other = lists:delete(ServerName, erlang:nodes()),
io:format("Client '~p' connected to server '~p'.~nOnline users: ~p~n", [erlang:node(), ServerName, Other]),
erlang:register(client, spawn_link(fun print/0)),
client_loop(ServerName).
print() ->
receive
{msg, Name, Text} ->
io:format("~p: ~p~n", [Name, Text]),
print()
end.
client_loop(ServerName) ->
rpc:call(ServerName, ?MODULE, server_new_message, [erlang:node(), io:get_line(">>> ")]),
client_loop(ServerName).
%% Clients run this function in server's node
%% 'server' process will receive messages and print them and broadcast them
server_new_message(Name, Text) ->
server ! {msg, Name, Text}.
Run:
I open 3 Erlang shells.
In shell 1 i run server:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:server_start(local_chat_server).
Server 'local_chat_server#jahanbakhsh' started.
Messages:
Server is waiting for messages.
In shell 2 i run client 1:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_1).
Client 'client_1#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: []
>>>
Now i can send message from this terminal, but wait.
I run client 2 in shell 3:
p#jahanbakhsh ~/Desktop $ erl
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> test:client_start('local_chat_server#jahanbakhsh', client_2).
Client 'client_2#jahanbakhsh' connected to server 'local_chat_server#jahanbakhsh'.
Online users: [client_1#jahanbakhsh]
>>>
I send a message ("Test message") from shell 2 or client 1.
In shell 1 or server i have:
client_1#jahanbakhsh: "Test message\n" - sent to [client_2#jahanbakhsh]
In shell 3 or client 2 i have:
client_1#jahanbakhsh: "Test message\n"
>>>

How do unix domain sockets work in Erlang 19

I tried a few things but I'm not able to read anything from them
{ok, Port} = gen_udp:open(0, [{ifaddr,{local,"/tmp/socket2"}}]).
Then I switch to console.
echo "hi" | socat - UNIX-CONNECT:/tmp/socket2
Back to erlang
41> gen_udp:recv(Port, 2, 5000).
{error,timeout}
Any help is appreciated. I've also tried {active, true} opt and flush(). shows nothing.
I've not tried the official release 19, but I can make it work using the latest git (as of July 7th) by:
disabling active with {active, false}
using UNIX-SENDTO instead of UNIX-CONNECT
binding socat's socket to its own address (not binding creates an error on erlangs side when resolving the address.)
Demonstration:
console 1:
$ rm /tmp/socket*
$ erl
Erlang/OTP 19 [erts-8.0.1] [source-ca40008] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0.1 (abort with ^G)
1> {ok, Port} = gen_udp:open(0, [{active, false},{ifaddr, {local,"/tmp/socket2"}}]),
1> io:format("ok~w ~w~n", [ok,Port]),
1> gen_udp:recv(Port, 2).
okok #Port<0.451>
console 2:
$ echo "hi" | socat - UNIX-SENDTO:/tmp/socket2,bind=/tmp/socket1
console 1 results:
okok #Port<0.451>
{ok,{{local,<<"/tmp/socket1">>},0,"hi\n"}}

Copying files from one server to other via erlang code

I want to copy files from one server(Say A) to other(Say B).Scenerio is->User sends me a file(video,zip or photo) in binary.I write this to /var/www/myfolder of A.Now in next step i want this to be copied at B.I used os:cmd(scp ----) command.But it gives error like->
Permission denied, please try again.
Permission denied, please try again.
Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
I think it is asking for password of B.How can i configure password in scp command or is there any other method to do this in erlang?
You can use Erlang File I/O if you have both servers connected using erlang distribution protocol. (You can do it over TCP or UDP or any other networking as well but it is more complicated).
Let's demonstrate it using two "servers" running on the same machine (it works same over a network, but you have to connect them properly). First we make directory for each server and content of file foo to transfer:
$ mkdir a b
$ echo Hello World > a/foo
Let's start two servers each in a different directory:
$ cd a
a$ erl -sname a
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V7.0 (abort with ^G)
(a#hynek-notebook)1>
and the second server in a different console:
$ cd b
b$ erl -sname b
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V7.0 (abort with ^G)
(b#hynek-notebook)1>
Now we check there is nothing on the b server yet:
(b#hynek-notebook)1> ls().
ok
(b#hynek-notebook)2>
Now we can connect nodes, check if they are connected and read the file foo.
(a#hynek-notebook)1> net_kernel:connect('b#hynek-notebook').
true
(a#hynek-notebook)2> net_adm:ping('b#hynek-notebook').
pong
(a#hynek-notebook)3> ls().
foo
ok
(a#hynek-notebook)4> {ok, Bin} = file:read_file("foo").
{ok,<<"Hello World\n">>}
(a#hynek-notebook)5>
And write file to server b using rpc:call/4:
(a#hynek-notebook)5> rpc:call('b#hynek-notebook', file, write_file, ["bar", Bin]).
ok
(a#hynek-notebook)6>
Check the result on the server b:
(b#hynek-notebook)2> ls().
bar
ok
(b#hynek-notebook)3> {ok, Bin} = file:read_file("bar").
{ok,<<"Hello World\n">>}
(b#hynek-notebook)4>
For bigger files, you should not transfer a whole file in the one big binary. Unfortunately, you need at least some code support from sending or receiving side. You can transfer file handler from one node to another but the process which calls file:open/2 has to keep running. It is reason you can't just use {ok, FH} = rpc:call(Node, file, open, [FN, [write]]). It's a bummer. One way is to make a very simple server which opens a file and keeps running.
(b#hynek-notebook)4> Self = self().
<0.40.0>
(b#hynek-notebook)5> F = fun() -> Self ! file:open("baz", [write]), receive close -> ok end end.
#Fun<erl_eval.20.54118792>
(b#hynek-notebook)6> FS = spawn_link('a#hynek-notebook', F).
<7329.48.0>
(b#hynek-notebook)7> {ok, FH} = receive X -> X end.
{ok,<7329.49.0>}
(b#hynek-notebook)8> file:write(FH, Bin).
ok
(b#hynek-notebook)9> FS ! close.
close
(b#hynek-notebook)10>
And we expect file baz with proper content on the server a:
(a#hynek-notebook)6> ls().
baz foo
ok
(a#hynek-notebook)7> {ok, _} = file:read_file("baz").
{ok,<<"Hello World\n">>}
(a#hynek-notebook)8>
The other option is to write a server which will receive blocks and write them instead of sending a file handler. And there are many other options how to do it using direct TCP connection using HTTP or your own protocol and using file:send_file/2,5 and many other ways.

Why does net_kernel:monitor_nodes/2 not deliver nodeup/nodedown messages for sname nodes?

I start up a master node with a short name and get it running a process to monitor for node up and down messages.
> erl -sname master -cookie monster
Erlang R15B03 (erts-5.9.3) [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false] [dtrace]
Eshell V5.9.3 (abort with ^G)
(master#pencil)1> c("/tmp/monitor.erl").
{ok,monitor}
(master#pencil)2> Pid = monitor:start().
<0.44.0>
(master#pencil)3> Pid ! running.
RECV :: running
running
(master#pencil)4> net_adm:names().
{ok,[{"master",52564}]}
At this point only the master node is running. I startup the second node on the same machine:
> erl -sname client -cookie monster
Erlang R15B03 (erts-5.9.3) [source] [64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false] [dtrace]
Eshell V5.9.3 (abort with ^G)
(client#pencil)1>
and wait for a minute, just incase I'm reading the docs wrong and there's a complication with the net tickrate. Nothing, so on master I force the connection:
(master#pencil)5> net_adm:names().
{ok,[{"master",52564},{"client",52579}]}
(master#pencil)6>
and nothing from my little monitor process. Now, if I do the same thing but use long names--that is -name--this works just fine. I'm surprised, though, as the net_kernel docs don't mention that. What's the deal?
Here's the monitor.erl referenced above:
-module(monitor).
-export([start/0]).
start() ->
spawn_link(fun init_loop/0).
%%%===================================================================
%%% Internal Functions
%%%===================================================================
init_loop() ->
net_kernel:monitor_nodes(true, []),
loop().
loop() ->
receive
Msg -> io:format(user, "RECV :: ~p~n", [Msg])
end,
loop().
net_kernel:monitor_nodes/2 definitely does deliver nodeup/nodedown messages for nodes with either short and long names.
However, the nodeup message is only delivered when the node is connected, as mentioned in the documentation. Why you got the nodeup message with -name is a mystery (and couldn't be reproduced here) as net_adm:names/0 does not connect nodes at all. It only connects to epmd to obtain the list of locally registered nodes. It will even list nodes with a different cookie.
If you connect the client to the master (or the other way around) with net_adm:ping/1 (or an rpc call), the monitoring process will receive the nodeup message.

Erlang noob seeks quick code review

Hi all,
Very new to Erlang, coming from C/C++/Java. Been playing with code and have myself narrowed to a point where a second of guidance might save me a half a day. So what I have is a little telnet client which I'm intending to connect to a freeswitch esl port and let me issue commands to the port as I would in fs_cli. (I guess the main thing is…I'm trying to talk to a port that I should be able to communicate via telnet). The erlang app fails while a Linux telnet is working great. I'm sure the issue is simple and subtle; any help appreciated!
So, here's how the session goes using Linux telnet:
$>telnet localhost 8021
Trying localhost...
Connected to localhost.
Escape character is '^]'.
Content-Type: auth/request
auth password<ENTER>
<ENTER>
Content-Type: command/reply
Reply-Text: +OK accepted
log 1<ENTER>
<ENTER>
Content-Type: command/reply
Reply-Text: +OK log level 1 [1]
…Okay, here's my telnet client code:
-module(getty).
-export([s/0]).
%-define(LISTEN_PORT, 9000).
%-define(TCP_OPTS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, once}]).
s() ->
case gen_tcp:connect( "localhost", 8021,[{active,false},{packet,2}]) of
{ok,Sock} ->
io:format("~p Connected to localhost 8021.~n", [erlang:localtime()] ),
main_loop( Sock );
Error ->
io:format("Error: ~p~n", [Error])
end.
main_loop( Sock ) ->
Command = get_user_input( "Command> " ),
spawn(fun() -> ex_cmd( Sock, Command ) end),
main_loop( Sock ).
ex_cmd(Sock, Command) ->
B = gen_tcp:recv( Sock, 0 ),
io:format( "Response: ~p~n", [B] ),
gen_tcp:send( Sock, Command ),
A = gen_tcp:recv( Sock, 0 ),
%gen_tcp:close( Sock ),
io:format( "Response: ~p~n", [A] ).
get_user_input( Prompt ) ->
A1 = string:concat(
string:strip( % remove spaces from front and back
string:strip( % remove line-feed from the end
io:get_line( Prompt ), right, $\n)), "\r\n\r\n" ),
io:format( "Command is: ~p~n", [A1] ),
A1.
…here's a run of using the erlang client:
$>erl
Erlang R15B01 (erts-5.9.1) [source] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> c(getty).
{ok,getty}
2> getty:s().
{{2012,7,12},{10,15,0}} Connected to localhost 8021.
Command> auth password
Command is: "auth password\r\n\r\n"
Response: {error,closed}
Response: {error,closed}
Command>
Any clues on the different result using the erlang client? TIA!
By using {packet,2}, you're claiming that packets will be sent with a 2-byte header declaring the size of the packet, and that you're expecting the server to send such headers as well. Telnet does not do this, so if you're trying to emulate a telnet client, don't specify a packet mode of 2. Instead, use 0 or raw for the packet type to specify no header. I believe that leaving off the packet option defaults to no header as well.

Resources