-
Notifications
You must be signed in to change notification settings - Fork 0
/
answers.txt
220 lines (180 loc) · 11.1 KB
/
answers.txt
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
Student : The Duy Nguyen
Login ID : theduyn
Student ID : 1100548
=======================================
PART 3.3
---------
Q1:
The server perhaps should not accept calls from everyone. This is primarily due to security reasons.
Since the transport layer protocol used - TCP - does not provide any data encryption, if a client
asks to receive server's sensitive data or sends their sensitive data to the server, it may easily be
sniffed. So the subset of clients should be those who understand the security risks and what data is
considered sensitive on their end as well as on the server.
---------
Q2:
Authentication should be the framework's responsibility. This is because authentication can be viewed
in a request-response way (meaning a client requests to authenticate, the server responds with the
required information for authentication, and the client receives that and accordingly responds). This
is, in essence, what our RPC is doing. So it already provides with a convenient interface for the
framework to deploy authentication. It should be the framework's responsibility to actualize it using
RPC.
That said, it is also the RPC's responsibility to ensure that the framework's authentication can be
actualized with the required security. Perhaps, it may be better if RPC uses TCP on SSL or other means
to support the framework.
---------
Q3:
TCP was the used protocol. There are several strengths in using TCP:
1. It is industry standard, and likewise very practical.
2. It is reliable, since it deals well with packet loss, error recovery, among other issues when
transmitting packets over the network.
3. It is stream-oriented, meaning TCP will segment the data however is required for the users.
In contrast to UDP, however, while the mentioned points are its strengths, UDP is better at:
1. Being more lightweight (lighter header size, minimal error recovery check, etc.), meaning less
overhead and faster in speed.
2. Being connectionless, meaning it is not restricted to a pair of client and user, and one end system
can therefore send lots of packets to others.
3. Multiplexing - data immediacy
That said, the strengths of TCP mentioned above is what UDP does not have, and all of which are deemed
of great importance for the nature of the client-server model in the project.
Compare to QUIC, on the other hand, TCP may pale in comparison.
1. QUIC ensures security with encryption and other security measures.
2. QUIC is faster than TCP, as its aim is to reduce latency. Specifically, it reduces the handshake
protocol in TCP, and among other techniques.
3. It utilizes multiplexing with push.
As of now, QUIC has not been as widely used as TCP. For the purpose of benefiting from industry
standard with widespread and well-known implementations, I have chosen TCP as the protocol.
---------
Q4:
The server's listen socket should be created in rpc_server_init.
The server's client accept socket should be created in rpc_serve_all. Unlike listen socket, once serve
all is called, it should only care about accepting connections from clients and respond to them.
The client's connect socket should be created in rpc_client_init to establish that it either has or
has not been able to connect to a server. Subsequently, client should only concern about requests.
---------
Q5:
rpc_client should be dynamically allocated primarily because they must be able to close their
connections and have the represented structure memory deallocated to give space to other things. With
static memory, this won't be possible.
rpc_server in theory could be statically allocated since it is "always on". However, dynamic allocation
allows for more convenient argument passing and type returning in functions which may be beneficial. It
is safe to assume that several fields related to the server may be used by various functions. As such,
it is perhaps more practical to dynamically allocate the rpc_server.
---------
Q6:
Regardless of whether which end system uses which byte ordering, all it should be concerned about is
converting its byte ordering to the network's when sending over the network, and convert the byte
ordering of the data received on the network back to its ordering. Therefore, in theory, this should
not pose a significant issue if we do this simple system-to-network and network-to-system conversion.
=======================================
PART 3.4
RPC Protocol Description:
Preface
-------------
The Remote Procedure Protocol provides an ease of use interface for the client to call a function
as if locally, but is actually making a request to the server. It also provides an interface for the
server to handle the requests and provides responses to the clients.
Transport Layer Protocol: TCP on IPv6
-------------
The RCP protocol uses TCP, mainly for reliability. This ensures that the data packets are as reliably
transmitted as possible. Specifically, TCP ensures that IP layer packet loss and duplication are
appropriately handled. Moreover, it also handles the restriction of maximum allowed size of IP packets
due to its stream-oriented nature. This is because TCP will already have fragmented large packets into
smaller streams of bytes so as to not exceed Maximum Transmission Unit.
It is also important to note that the TCP protocol of our RPC protocol runs on IPv6.
Multi-threaded
-------------
The RPC protocol is designed with multi-threaded server in mind. For this reason, multiple clients
may connect to the same server at once.
Server usage:
-------------
1. Creation:
On the server's end, the server must first initialize the server RPC by calling:
rpc_server* rpc_init_server(int port);
As seen in the function's signature, it is expected of the server to provide the server RPC
initialization with an appropriate port number.
2. Registration:
The server then must register the functions that clients can request from. Once the server is "on"
(we will discuss this afterwards), functions cannot be registered anymore unless the server is
down. The function call is:
int rpc_register(rpc_server* server, char* name, rpc_handler handler);
The handler is a function local to the server, and would be part of client's requests once the
server is on. It is assigned with a name. The return value indicates whether handler registration
was successful.
3. Serve:
This is when the server is officially on. At this stage, the server will start accepting client's
connections and will be permanently "on" unless it was manually killed. The function call is:
void rpc_serve_all(rpc_server* server);
The type of requests that it will serve will be detailed in the Client usage section.
Client usage:
-------------
1. Connect:
On the client's end, the client is expected to connect to the server themselves with the client
RPC initialization function:
rpc_client* rpc_init_client(char* addr, int port);
To connect, the client must provide:
- addr: the IP address, in IPv6
- port: the port number
2. Find request:
The first type of request the client could make is a find. The find request is simply to find the
function on the server:
rpc_handle* rpc_find(rpc_client* client, char* name);
The function requires the name for the function. Once called, the server will send a handle back
to the client. The handle is a placeholder for the function that the client requests to find.
If the name is not registered on the server, that handle will be NULL. Otherwise, it will be the
proper function placeholder that allows client to call "locally" on their system.
3. Call request:
With the handle provided, the client can now make the function call with the call request:
rpc_data* rpc_call(rpc_client* client, rpc_handle* handle, rpc_data* payload);
The requirements for the function handle call is the payload. In other words, the handle
technically takes the payload as an argument. As such, the client must provide with the handle
they receive, and the payload argument for the handle.
The returned object is the response from the server. Both of which have the same structure.
NOTE:
Due to the un-local nature of the function call, the payload and response are from different
ends. Hence, this may pose issues with the size capable for receiving of a specific system.
As such, packets exceeding the limit size will alert both end systems with "Overlength error"
message onto the standard error.
Read more at Shared usage and Routine failures segments below.
4. Stop the connection:
Once all has been requested, the client must stop their connection to the server:
void rpc_close_client(rpc_client* client);
This will free the client RPC and close their connection to the server.
Shared usage:
-------------
As discussed in Client usage segment, the rpc_data structure type acts as both the payload for the
client to pass to their function call, and the response given to the client as a result of said call.
It should be noted that this is not actual local client function call, meaning the response actually
comes from the server. In short, it is the RPC protocol-specific data packet to be sent over the
network. So it is both the client's and server's responsibilities to free the RPC data structure:
void rpc_data_free(rpc_data* data);
It is important to note that the RPC data packet must be able to 'fit' to the other end system for
successful transmission. Here is the structure of RPC data:
typedef struct {
int data1;
size_t data2_len;
void* data2;
} rpc_data;
Notably, data2 size is defined by a size_t data2_len variable, which is system dependent. The RPC
protocol ensures that data2_len shall not exceed either end system's maximum size_t value (SIZE_MAX).
If it does, the recipient will not be able to receive the packet, and both end system will receive
an error message "Overlength error".
Routine failures:
-------------
Overlength error:
For details, to ensure that the size_t does not exceed SIZE_MAX, the protocol will first send and
receive the UINT_MAX value from one another. This is a number that can assuredly be sent over the
network (the protocol provides with sending up to 64-bit integers). Then it will pick the smaller
UINT_MAX from the 2 systems as the 'pivot'. Then, using the pivot, within the sender's system, we
check for:
n_s: the number of times data2_len has to be sent if pivot was the ceiling value
r_s: the remainder of that last sending
The sender will send these 2 values to the receiver's end. The receiver's end will then do the same
for their own SIZE_MAX:
n_r: the number of chunks if SIZE_MAX were to be divided by pivot
r_r: the remainder
With this, the receiver of data2 must make sure that, either n_r > n_s, or they are equal and that
r_r > r_s. If this does not satisfy, then that means data2 has exceeded the packet limit and thus
cannot be transmitted to the receiver's end.
Other failure checks:
In requesting server's services, the client and server must both exchange flags for verification.
If the service doesn't exist, then the client simply fails to request and they both move on.