I followed this method to read files - how to read the contents of a file In Erlang?
The answer mentioned works good. However, my file content looks like this:
{0, {data, node, 0}}
{1, {someData, node, 1}}
Every line will have this data. So while reading file I want to filter on how many lines it should read
something like this:
read(Node, FirstIndex, LastIndex, F, Acc)
Here, F is the fold function, which takes a single log entry and the current accumulator and returns the new accumulator value.
How can I incorporate the solution with this function that I need??
-module(my).
-compile(export_all).
get_section(Start, End, Fname) when Start>0, End>=Start ->
{ok, Io} = file:open(Fname, read),
ok = advance_file_pointer(Start, Io),
Lines = getNLines(End-Start+1, Io),
file:close(Io),
Lines.
advance_file_pointer(1, _Io) ->
ok;
advance_file_pointer(Start, Io) ->
{ok, _Line} = file:read_line(Io),
advance_file_pointer(Start-1, Io).
getNLines(N, Io) ->
getNLines(N, Io, []).
getNLines(0, _Io, Acc) ->
lists:reverse(Acc);
getNLines(N, Io, Acc) ->
{ok, Line} = file:read_line(Io),
getNLines(N-1, Io, [Line|Acc]).
In the shell:
~/erlang_programs$ cat data.txt
{1, {data, node, 1}}
{2, {someData, node, 2}}
{3, {otherData, node, 3}}
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:get_section(1, 1, "data.txt").
["{1, {data, node, 1}}\n"]
3> my:get_section(1, 2, "data.txt").
["{1, {data, node, 1}}\n","{2, {someData, node, 2}}\n"]
4> my:get_section(1, 3, "data.txt").
["{1, {data, node, 1}}\n","{2, {someData, node, 2}}\n",
"{3, {otherData, node, 3}}\n"]
5> my:get_section(2, 3, "data.txt").
["{2, {someData, node, 2}}\n","{3, {otherData, node, 3}}\n"]
6> my:get_section(2, 2, "data.txt").
["{2, {someData, node, 2}}\n"]
7> my:get_section(0, 2, "data.txt").
** exception error: no function clause matching my:get_section(0,2,"data.txt") (my.erl, line 4)
8> my:get_section(1, 4, "data.txt").
** exception error: no match of right hand side value eof
in function my:getNLines/3 (my.erl, line 20)
in call from my:get_section/3 (my.erl, line 7)
9>
You can apply your fold function in the second clause of getNLines/3.
Related
I'm fairly new to Erlang, and I'm trying to set up a simple multi-client chat server to learn more about the language. However whenever I try and run it I get a socket closed error from the call of gen_tcp:accept on the listening socket. I've tried a number of different port numbers to no avail. What am I missing?
The code is below:
-define(TCP_OPTIONS, [binary, {packet, 2}, {active, false}, {reuseaddr, true}]).
listen(Portno, DictPid) ->
case gen_tcp:listen(Portno, ?TCP_OPTIONS) of
{ok, ListenSocket} ->
spawn_link(fun() -> accept_connections(ListenSocket, DictPid) end),
io:format("Listening on ~p~n", [ListenSocket]);
{error, Error} ->
io:format("Listen Error: ~w~n", [Error])
end.
accept_connections(ListenSocket, DictPid) ->
case gen_tcp:accept(ListenSocket) of
{ok, ClientSocket} ->
io:format("Accepting:~w~n", [ClientSocket]),
gen_tcp:send(ClientSocket, "Welcome! Enter your name~n"),
ClientPid = spawn(fun() -> io:format("Client connected"),
setup_user(ClientSocket, DictPid) end),
gen_tcp:controlling_process(ClientSocket, ClientPid),
accept_connections(ListenSocket, DictPid);
{error, Error} ->
io:format("Accept Error: ~w~n", [Error])
end.
setup_user(ClientSocket, DictPid) ->
{ok, Username} = gen_tcp:recv(ClientSocket, 0),
DictPid ! {add_new_pair, ClientSocket, Username},
ClientDict = get_dict(DictPid),
broadcast_message(dict:fetch_keys(ClientDict), "[" ++ Username ++ "has entered the chat]"),
client_loop(ClientSocket, Username, DictPid).
[rest of program]
The issue here is that the controller of the ListenSocket terminates, which causes the ListenSocket to be closed.
So, I am trying to read and write into a file.
While writing into the file, I need to check if a particular index exist in file then I don't write and throw error.
The data in file will look like this:
{1,{data,dcA,1}}.
{2, {data, dcA, 2}}.
{3,{data,dcA,3}}.
I added the dot at the end of each line because file:consult() needs the file like this.
Which is in this format.
{Index, {Data, Node, Index}}
When I have to add a new file, I check with this Index.
Here's what I have tried so far - https://pastebin.com/apnWLk45
And I run it like this:
193> {ok, P9} = poc:start(test1, self()).
{ok,<0.2863.0>}
194> poc:add(P9, Node, {6, data}).
In poc:add/3, P9 is the process id from the file:open.
I defined before in shell as dcA
And the third is the data - which is in this format - {Index, data}
Since I am using file:consult/1, it takes the filename as parameter. At that point, I only have process id. So I take the name from
file:pid2name(_Server).
This runs perfectly when I run it for the first time.
When I run this again - poc:add(P9, Node, {6, data2}), I get an error in this line file:pid2name(_Server).
exception error: no match of right hand side value undefined
How can I solve this issue?
I am new to Erlang. Just been a week that I started learning.
I am trying to read and write into a file. While writing into the
file, I need to check if a particular index exist in file then I don't
write and throw error.
A DETS table can easily do what you want:
-module(my).
-compile(export_all).
open_table() ->
dets:open_file(my_data, [{type, set}, {file, "./my_data.dets"}]).
close_table() ->
dets:close(my_data).
clear_table() ->
dets:delete_all_objects(my_data).
insert({Key, _Rest}=Data) ->
case dets:member(my_data, Key) of
true -> throw(index_already_exists);
false -> dets:insert(my_data, Data)
end.
all_items() ->
dets:match(my_data, '$1').
In the shell:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:open_table().
{ok,my_data}
3> my:clear_table().
ok
4> my:all_items().
[]
5> my:insert({1, {data, a, b}}).
ok
6> my:insert({2, {data, c, d}}).
ok
7> my:insert({3, {data, e, f}}).
ok
8> my:all_items().
[[{1,{data,a,b}}],[{2,{data,c,d}}],[{3,{data,e,f}}]]
9> my:insert({1, {data, e, f}}).
** exception throw: index_already_exists
in function my:insert/1 (my.erl, line 15)
When I run this again - poc:add(P9, Node, {6, data2}), I get an error
in this line file:pid2name(_Server):
exception error: no match of right hand side value undefined
When a process opens a file, it becomes linked to a process that handles the file I/O, which means that if the process that opens the file terminates abnormally, the I/O process will also terminate. Here is an example:
-module(my).
-compile(export_all).
start() ->
{ok, Pid} = file:open('data.txt', [read, write]),
spawn(my, add, [Pid, x, y]),
exit("bye").
add(Pid, _X, _Y) ->
timer:sleep(1000), %Let start() process terminate.
{ok, Fname} = file:pid2name(Pid),
io:format("~s~n", [Fname]).
In the shell:
1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
2> my:start().
** exception exit: "bye"
in function my:start/0 (my.erl, line 7)
3>
=ERROR REPORT==== 25-Jun-2018::13:28:48 ===
Error in process <0.72.0> with exit value:
{{badmatch,undefined},[{my,add,3,[{file,"my.erl"},{line,12}]}]}
According to the pid2name() docs:
pid2name(Pid) -> {ok, Filename} | undefined
the function can return undefined, which is what the error message is saying happened.
I'm trying to make a recursive call to handle_call/3 before replying.
But it seems to not be possible, because a timeout exit exception is thrown.
Below you can see the code and the error.
The code:
-module(test).
-behavior(gen_server).
%% API
-export([start_link/0,init/1,handle_info/2,first_call/1,second_call/1, handle_call/3, terminate/2]).
-record(state, {whatever}).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_Args) ->
{ok, #state{}}.
handle_info(Data, State) ->
{noreply, State}.
% synchronous messages
handle_call(_Request, _From, State) ->
case _Request of
first_call ->
{reply, data1, State};
second_call ->
{reply, {data2, first_call(self())}, State}
end.
first_call(Pid) ->
gen_server:call(Pid, first_call).
second_call(Pid) ->
gen_server:call(Pid, second_call).
terminate(_Reason, _State) ->
ok.
The error:
2> {_, PID} = test:start_link().
{ok,<0.64.0>}
3> test:second_call(PID).
** exception exit: {timeout,{gen_server,call,[<0.64.0>,second_call]}}
in function gen_server:call/2 (gen_server.erl, line 204)
A possible reason for this behaviour is that gen_server is not able to handle recursive calls until the first one is done (creating a deadlock). Is this the right reason, and if yes why?
Thanks.
Yes, that is the reason. The gen_server is a single process, and while handle_call is executing it doesn't pick up any messages nor respond to any gen_server:call. Therefore first_call(self()) times out.
I just do a testing with gen_tcp. One simple echo server, and one client.
But client started and closed, server accept two connection, and one is ok, the other is bad.
Any issue of my demo script, and how to explain it?
server
-module(echo).
-export([listen/1]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(LSocket).
accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
spawn(fun() -> loop(Socket) end),
accept(LSocket).
loop(Socket) ->
timer:sleep(10000),
P = inet:peername(Socket),
io:format("ok ~p~n", [P]),
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} ->
ok;
E ->
io:format("bad ~p~n", [E])
end.
Demo Server
1> c(echo).
{ok,echo}
2> echo:listen(1111).
ok {ok,{{192,168,2,184},51608}}
ok {error,enotconn}
Client
> spawn(fun() -> {ok, P} = gen_tcp:connect("192.168.2.173", 1111, []), gen_tcp:send(P, "aa"), gen_tcp:close(P) end).
<0.64.0>
```
But client started and closed, server accept two connection, and one is ok, the other is bad.
Actually, your server only accepted one connection:
Enter loop/1 upon accepting the connection from the client
inet:peername/1 returns {ok,{{192,168,2,184},51608}} because the socket is still open
gen_tcp:recv/2 returns <<"aa">> which was sent by the client
gen_tcp:send/2 sends the data from 3 to the client
Enter loop/1 again
inet:peername/1 returns {error,enotconn} because the socket was closed by the client
gen_tcp:recv/2 returns {error,closed}
The process exits normally
So in reality, your echo server is functioning just fine, however there are some improvements that can be made that are mentioned in the comment made by #zxq9.
Improvement 1
Hand off control of the accepted socket to the newly spawned process.
-module(echo).
-export([listen/1]).
-define(TCP_OPTIONS, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]).
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(LSocket).
accept(LSocket) ->
{ok, CSocket} = gen_tcp:accept(LSocket),
Ref = make_ref(),
To = spawn(fun() -> init(Ref, CSocket) end),
gen_tcp:controlling_process(CSocket, To),
To ! {handoff, Ref, CSocket},
accept(LSocket).
init(Ref, Socket) ->
receive
{handoff, Ref, Socket} ->
{ok, Peername} = inet:peername(Socket),
io:format("[S] peername ~p~n", [Peername]),
loop(Socket)
end.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("[S] got ~p~n", [Data]),
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} ->
io:format("[S] closed~n", []);
E ->
io:format("[S] error ~p~n", [E])
end.
Improvement 2
Wait on the client side for the echo server to send back the data before closing the socket.
spawn(fun () ->
{ok, Socket} = gen_tcp:connect("127.0.0.1", 1111, [binary, {packet, 0}, {active, false}]),
{ok, Peername} = inet:peername(Socket),
io:format("[C] peername ~p~n", [Peername]),
gen_tcp:send(Socket, <<"aa">>),
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
io:format("[C] got ~p~n", [Data]),
gen_tcp:close(Socket);
{error, closed} ->
io:format("[C] closed~n", []);
E ->
io:format("[C] error ~p~n", [E])
end
end).
Example
The server should look something like this:
1> c(echo).
{ok,echo}
2> echo:listen(1111).
[S] peername {{127,0,0,1},57586}
[S] got <<"aa">>
[S] closed
The client should look something like this:
1> % paste in the code from Improvement 2
<0.34.0>
[C] peername {{127,0,0,1},1111}
[C] got <<"aa">>
Recommendations
As #zxq9 mentioned, this is not OTP style code and probably shouldn't be used for anything beyond educational purposes.
A better approach might be to use something like ranch or gen_listener_tcp for the server side listening and accepting of connections. Both projects have examples of echo servers: tcp_echo (ranch) and echo_server.erl (gen_listener_tcp).
I'm currently trying to create a simple chat server with socket.io-erlang. I just started learning Erlang so I have a few problems adapting their demo so it works with modules. Hope you can help me, here's what I have so far. It shouldn't have any features yet, this time I only want to make it work (I get a few crash reports after starting it, you can read them if you want).
App
-module(echat_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
echat_sup:start_link().
stop(_State) ->
ok.
Supervisor
-module(echat_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
application:start(sasl),
application:start(gproc),
application:start(misultin),
application:start(socketio),
{ok, Pid} = socketio_listener:start([{http_port, 7878}, {default_http_handler,echat_http_handler}]),
EventManager = socketio_listener:event_manager(Pid),
ok = gen_event:add_handler(EventManager, echat_socketio_handler,[]),
receive _ ->
io:format("sub received something"),
{ok, {
{one_for_one, 5, 10},
[]
}}
end.
Socket.IO Event Handler
-module(echat_socketio_handler).
-behaviour(gen_event).
-include_lib("socketio/include/socketio.hrl").
-export([init/1, handle_event/2, handle_call/2, handle_info/2, terminate/2, code_change/3]).
init([]) ->
{ok, undefined}.
handle_event({client, Pid}, State) ->
io:format("Connected: ~p~n",[Pid]),
EventManager = socketio_client:event_manager(Pid),
ok = gen_event:add_handler(EventManager, ?MODULE,[]),
{ok, State};
handle_event({disconnect, Pid}, State) ->
io:format("Disconnected: ~p~n",[Pid]),
{ok, State};
handle_event({message, Client, Msg=#msg{content=Content}}, State) ->
io:format("Got a message: ~p from ~p~n",[Msg, Client]),
socketio_client:send(Client, #msg{ content = "hello!" }),
socketio_client:send(Client, #msg{ content = [{<<"echo">>, Content}], json = true}),
{ok, State};
handle_event(_E, State) ->
{ok, State}.
handle_call(_, State) ->
{reply, ok, State}.
handle_info(_, State) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
HTTP Request Handler
-module(echat_http_handler).
-export([handle_request/3]).
handle_request(_Method, _Path, Req) ->
Req:respond(200).
just a few things, no a specific answer to your question.
First, in general you start the dependencies before starting your app, rather than on the init of some supervisor.
Regarding the errors:
{error,{{noproc,{gen_server,call,
[socketio_listener_sup_sup,
{start_child,[[{http_port,7878},
{default_http_handler,echat_http_handler}]]},
infinity]}},
{echat_app,start,[normal,[]]}}}
This is the first one, it means that the code tried to call a gen_server named 'socketio_listener_sup_sup' , but that process did not exist. Given the name of it, I guess that is something that should be started by the socket.io-erlang application itself, so maybe it is not starting correctly for some reason. You can check that easily by checking the results:
ok = application:start(sasl),
ok = application:start(gproc),
ok = application:start(misultin),
ok = application:start(socketio),