-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserverA.cpp
More file actions
283 lines (258 loc) · 8.17 KB
/
serverA.cpp
File metadata and controls
283 lines (258 loc) · 8.17 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
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/**
* Reused code:
* Chapter5 of Beej's Code: http://www.beej.us/guide/bgnet/
* 1. create/bind UDP socket
* 2. send/receive UDP message
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define FAIL -1
#define MAX_DATA_SIZE 1024
#define SERVER_HOST "127.0.0.1"
#define CLIENT_HOST "127.0.0.1"
#define SERVER_PORT 21800
#define CLIENT_PORT 24800
#define SERVER_NAME "ServerA"
#define FILE_NAME "block1.txt"
vector<string> parse_string(string str, char c);
/**
* Data object for storing one transaction record
*/
class Data {
public:
int serial_num;
string sender;
string receiver;
int amount;
string my_str;
Data() {}
Data(string str) {
my_str = str;
vector<string> v;
v = parse_string(str, ' ');
serial_num = stoi(v[0]);
sender = v[1];
receiver = v[2];
amount = stoi(v[3]);
}
};
/**
* print error message and exit when the socket related function is failed
*/
void socket_error_checking(int status, string str) {
const char *error_msg = str.c_str();
if (status == FAIL) {
perror(error_msg);
exit(1);
}
}
/**
* create UDP server socket
*/
int create_udp_server_socket() {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
socket_error_checking(sockfd, "UDP socket");
return sockfd;
}
/**
* store the UDP server socket address information into the struct sockaddr_in * data type(include IPv4, server IP address and server port number)
*/
struct sockaddr_in set_server_addr() {
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
server_addr.sin_port = htons(SERVER_PORT);
return server_addr;
}
/**
* store the UDP client socket address information into the struct sockaddr_in * data type(include IPv4, client IP address and client port number)
*/
struct sockaddr_in set_client_addr() {
struct sockaddr_in client_addr;
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sin_family = AF_INET;
client_addr.sin_addr.s_addr = inet_addr(CLIENT_HOST);
client_addr.sin_port = htons(CLIENT_PORT);
return client_addr;
}
/**
* UDP bind server socket
*/
void udp_bind(int sockfd, struct sockaddr_in server_addr) {
int res = ::bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
socket_error_checking(res, "UDP bind");
}
/**
* UDP respond to the client
*/
void sendto_client(int client_sockfd, struct sockaddr_in client_addr, string msg) {
char send_buf[MAX_DATA_SIZE];
strcpy(send_buf, msg.c_str());
int res = sendto(client_sockfd, send_buf, sizeof(send_buf), 0, (struct sockaddr *) &client_addr, sizeof(client_addr));
socket_error_checking(res, "UDP sendto");
cout << "The " << SERVER_NAME << " finished sending the response to the Main Server." << endl;
}
/**
* UDP receive message from client and return the received message
*/
string receive_msg(int server_sockfd, struct sockaddr_in client_addr) {
char recv_buf[MAX_DATA_SIZE];
socklen_t client_addr_size = sizeof(client_addr);
int res = recvfrom(server_sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *) &client_addr, &client_addr_size);
socket_error_checking(res, "UDP recvfrom");
cout << "The " << SERVER_NAME << " received a request from the Main Server." << endl;
string recv_msg = recv_buf;
return recv_msg;
}
/**
* split a string with character c
*/
vector<string> parse_string(string str, char c) {
vector<string> v;
stringstream ss(str);
while (ss.good()) {
string substr;
getline(ss, substr, c);
if (substr != "") {
v.push_back(substr);
}
}
return v;
}
/**
* read the transaction record from log file and stored in the Data object
*/
void read_file(vector<Data> *data_list) {
string line;
fstream myfile(FILE_NAME);
if(myfile.is_open()) {
while (getline(myfile, line)) {
if (line == "") {
continue;
}
Data data(line);
data_list->push_back(data);
}
myfile.close();
} else {
cout << "Can not open log file" << endl;
system("pause");
}
}
/**
* write the transaction record into log file
*/
void write_file(string new_transaction) {
ofstream myfile;
myfile.open(FILE_NAME, ios::app);
myfile << new_transaction << endl;
myfile.close();
}
/**
* generate an unordered_map to map username to all corresponding transaction data
*/
void gen_username_map(vector<Data> *data_list, unordered_map<string, vector<Data> > *map) {
for (auto& data : *data_list) {
map->insert(make_pair(data.sender, vector<Data>()));
map->insert(make_pair(data.receiver, vector<Data>()));
map->at(data.sender).push_back(data);
map->at(data.receiver).push_back(data);
}
}
/**
* update the username map and transaction list by adding new transaction
*/
void insert_new_data(vector<Data> *data_list, string new_transaction, unordered_map<string, vector<Data> > *map) {
data_list->push_back(Data(new_transaction));
Data data(new_transaction);
map->insert(make_pair(data.sender, vector<Data>()));
map->insert(make_pair(data.receiver, vector<Data>()));
map->at(data.sender).push_back(data);
map->at(data.receiver).push_back(data);
}
/**
* check balance
*/
string check_balance(string names, unordered_map<string, vector<Data> > *username_map) {
vector<string> name_list = parse_string(names, ' ');
string msg = "";
unordered_set<string> user_data;
for (auto& name : name_list) {
if (username_map->find(name) != username_map->end()) {
for (auto& data : username_map->at(name)) {
user_data.insert(data.my_str);
}
}
}
for (auto& data : user_data) {
msg += (data + ",");
}
return msg;
}
/**
* get all transaction records
*/
string get_records(vector<Data> *data_list) {
string msg = "";
for (auto& t : *data_list) {
msg += (t.my_str + ",");
}
return msg;
}
/**
* get maximum serial number
*/
string get_max_serial_num(vector<Data> *data_list) {
int max_serial_num = 0;
for (auto& data : *data_list) {
max_serial_num = max(max_serial_num, data.serial_num);
}
return to_string(max_serial_num);
}
int main() {
cout << "The " << SERVER_NAME << " is up and running using UDP on port " << SERVER_PORT << "." << endl;
// create udp server socket and bind it's socket address
int server_sockfd = create_udp_server_socket();
struct sockaddr_in server_addr = set_server_addr();
udp_bind(server_sockfd, server_addr);
// read transactions data from file and store them in the vector and unordered_map
vector<Data> data_list;
unordered_map<string, vector<Data> > username_map;
read_file(&data_list);
gen_username_map(&data_list, &username_map);
while (true) {
struct sockaddr_in client_addr = set_client_addr();
string recv_msg = receive_msg(server_sockfd, client_addr);
vector<string> operation = parse_string(recv_msg, ',');
if (operation[0] == "INQUIRY") {
sendto_client(server_sockfd, client_addr, check_balance(operation[1], &username_map));
} else if (operation[0] == "TXLIST") {
sendto_client(server_sockfd, client_addr, get_records(&data_list));
} else if (operation[0] == "WRITE") {
write_file(operation[1]);
insert_new_data(&data_list, operation[1], &username_map);
sendto_client(server_sockfd, client_addr, "Already write in the log file!");
} else if (operation[0] == "NUM") {
sendto_client(server_sockfd, client_addr, get_max_serial_num(&data_list) + ",");
}
}
close(server_sockfd);
return 0;
}