-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclient.erl
More file actions
158 lines (134 loc) · 5.02 KB
/
client.erl
File metadata and controls
158 lines (134 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
%% - Client module
%% - The client module creates a parallel process by spawning handler.
%% - The handler does the following:
%% 1/ It makes itself into a system process in order to trap exits.
%% 2/ It creates a window and sets up the prompt and the title.
%% 4/ It waits for connection message (see disconnected).
%%
-module(client).
-import(window, [set_title/2, insert_str/2, set_prompt/2]).
-export([start/1]).
start(Host) ->
spawn(fun() -> handler(Host) end).
%%%%%%%%%%%%%%%%%%%%%%% INACTIVE CLIENT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% - The handler starts a window and a connector
handler(Host) ->
process_flag(trap_exit, true),
Window = window:start(self()),
set_title(Window, "Connecting..."),
set_prompt(Window, "action > "),
start_connector(Host),
disconnected(Window).
%% - The window is disconnected until it received a connected meassage from
%% the connector
disconnected(Window) ->
receive
{connected, ServerPid} ->
insert_str(Window, "Connected to the transaction server\n"),
set_title(Window, "Connected"),
connected(Window, ServerPid);
{'Exit', _, _} -> exit(died);
Other -> io:format("client disconnected unexpected:~p~n",[Other]),
disconnected(Window)
end.
%%%%%%%%%%%%%%%%%%%%%%% INACTIVE CLIENT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% CONNECTOR %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_connector(Host) ->
S = self(),
spawn_link(fun() -> try_to_connect(S,Host) end).
try_to_connect(Parent, Host) ->
%% Parent is the Pid of the process (handler) that spawned this process
{transaction_server, Host} ! {login, self(), Parent},
receive
{ok, ServerPid} -> Parent ! {connected, ServerPid},
exit(connectorFinished);
Any -> io:format("Unexpected message~p.~n",[Any])
after 5000 ->
io:format("Unable to connect to the transaction server at node~p. Restart the client application later.~n",[Host])
end,
exit(serverBusy).
%%%%%%%%%%%%%%%%%%%%%%% CONNECTOR %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%% ACTIVE CLIENT %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
connected(Window, ServerPid) ->
receive
%% - The user has requested a transaction
{request, Window, Transaction} ->
io:format("Client requested the transaction ~p.~n",[Transaction]),
insert_str(Window, "Processing request...\n"),
process(Window, ServerPid, Transaction);
{'EXIT', Window, windowDestroyed} -> end_client(ServerPid);
{close, ServerPid} -> exit(serverDied);
Other ->
io:format("client active unexpected: ~p~n",[Other]),
connected(Window,ServerPid)
end.
%% - Asking to process a request
process(Window, ServerPid, Transaction) ->
ServerPid ! {request, self()}, %% Send a request to server and wait for proceed message
receive
{proceed, ServerPid} ->
send(Window, ServerPid, Transaction); %% received green light send the transaction.
{close, ServerPid} -> exit(serverDied);
Other ->
io:format("client active unexpected: ~p~n",[Other])
end.
%% - Sending the transaction and waiting for confirmation
send(Window, ServerPid, []) ->
ServerPid ! {confirm, self()}, %% Once all the list (transaction) items sent, send confirmation
receive
{abort, ServerPid} -> insert_str(Window, "Aborted... type run if you want to try again!\n"),
connected(Window, ServerPid);
{committed, ServerPid} -> insert_str(Window, "Transaction succeeded!\n"),
connected(Window, ServerPid);
{'EXIT', Window, windowDestroyed} -> end_client(ServerPid);
{close, ServerPid} ->
exit(serverDied);
Other ->
io:format("client active unexpected: ~p~n",[Other])
end;
send(Window, ServerPid, [H|T]) ->
sleep(3),
case loose(6) of
%% In order to handle losses, think about adding an extra field to the
%message sent
false ->
ServerPid ! {action, self(), H};
true ->
io:format("dropped package~n"),
ok
end,
%Stop and wait
receive
{ack, ServerPid} ->
send(Window, ServerPid, T)
after 1000 ->
send(Window, ServerPid, [H|T])
end.
%%%%%%%%%%%%%%%%%%%%%%% Active Window %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% - Clean end
end_client(ServerPid) ->
io:format("Client ended communication.~n",[]),
ServerPid ! {close, self()},
exit(died).
%% - Blocks a random amount of seconds between 1 and 5.
%% - This simulates latency in the network.
%% - Latency is an integer parameter which can be interpreted as a worst case
%% waiting time in seconds
sleep(Latency) ->
receive
after 1000*random:uniform(Latency) ->
true
end.
%% - Loses messages randomly
%% - This simulates the fact that the communication media is unreliable
%% - Lossyness is an integer parameter:
%% - if Lossyness =0 the function will always return true
%% - if Lossyness >10 the function will always return false
%% - if lossyness =6 the function will return either values with
%% probability 1/2
loose(Lossyness) ->
Val=random:uniform(10),
if
Val >= Lossyness -> false;
true -> true
end.