-
Notifications
You must be signed in to change notification settings - Fork 0
/
sockethelper.cpp
256 lines (209 loc) · 8.57 KB
/
sockethelper.cpp
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
#include "sockethelper.h"
#include <unistd.h>
#include <sstream>
#include <sys/stat.h>
#include <arpa/inet.h>
SocketHelper::SocketHelper(string socketType, int backlogQueue) {
if (socketType.length() == 0) {
cerr << "Socket type ne peut être vide" << endl;
throw invalid_argument("SocketHelper constructeur : socketType");
}
this->socketType = socketType;
this->port = DEFAULT_PORT;
this->backlogQueue = backlogQueue;
}
void SocketHelper::setPort(int port) {
this->port = port;
}
void SocketHelper::setSocketFile(string socketFile) {
if (socketFile.length() == 0) {
cerr << "SocketFile ne peut être vide" << endl;
throw invalid_argument("SocketHelper.setSocketFile : socketFile");
}
this->socketFile = socketFile;
}
bool SocketHelper::isInet() {
return socketType.compare(SOCKET_INET) == 0;
}
bool SocketHelper::isUnix() {
return socketType.compare(SOCKET_UNIX) == 0;
}
bool SocketHelper::isUnknown() {
return !isInet() && !isUnix();
}
void SocketHelper::init() {
spdlog::debug("Initialisation de la socket ...");
if (this->isInet()) {
initSocketInet();
} else if (this->isUnix()) {
initSocketUnix();
} else {
spdlog::error("Type de socket non supportée");
throw invalid_argument("Type de socket non supportéé. Valid 'unix' et 'inet'");
}
// This listen() call tells the socket to listen to the incoming connections.
// The listen() function places all incoming connection into a backlog queue
// until accept() call accepts the connection.
listen(serverSockfd, backlogQueue);
// Default value without client are connected
clientSockfd = DEFAULT_SOCKET_FD;
}
void SocketHelper::initSocketInet() {
spdlog::info("Initialisation de la socket INET sur le port {}", this->port);
struct sockaddr_in serv_addr_inet;
// create a socket
serverSockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // socket(int domain, int type, int protocol)
int iSetOption = 1;
setsockopt(serverSockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&iSetOption, sizeof(iSetOption));
bzero((char *) &serv_addr_inet, sizeof(serv_addr_inet)); // clear address structure
/* setup the host_addr structure for use in bind call */
serv_addr_inet.sin_family = AF_INET; // server byte order
serv_addr_inet.sin_addr.s_addr = INADDR_ANY; // automatically be filled with current host's IP address
serv_addr_inet.sin_port = htons(this->port); // convert short integer value for port must be converted into network byte order
// bind(int fd, struct sockaddr *local_addr, socklen_t addr_length)
// bind() passes file descriptor, the address structure,
// and the length of the address structure
// This bind() call will bind the socket to the current IP address on port, portno
if (::bind(serverSockfd, (struct sockaddr *) &serv_addr_inet, sizeof(serv_addr_inet)) < 0) {
spdlog::error("Erreur sur bind() INET");
throw bad_function_call();
}
}
void SocketHelper::initSocketUnix() {
if (this->socketFile.length() == 0) {
spdlog::error("Nom de fichier de socket unix vide");
throw invalid_argument("Nom de fichier de socket unix vide");
}
removeSocketFile();
spdlog::info("Initialisation de la socket UNIX sur le fichier {}", this->socketFile);
struct sockaddr_un serv_addr_un;
// create a socket
serverSockfd = socket(AF_UNIX, SOCK_STREAM, 0); // socket(int domain, int type, int protocol)
bzero((char *) &serv_addr_un, sizeof(serv_addr_un)); // clear address structure
/* setup the host_addr structure for use in bind call */
serv_addr_un.sun_family = AF_UNIX; // server byte order
strcpy(serv_addr_un.sun_path, this->socketFile.c_str());
// bind(int fd, struct sockaddr *local_addr, socklen_t addr_length)
// bind() passes file descriptor, the address structure,
// and the length of the address structure
// This bind() call will bind the socket to the current IP address on port, portno
if (::bind(serverSockfd, (struct sockaddr *) &serv_addr_un, sizeof(serv_addr_un)) < 0) {
spdlog::error("Erreur sur bind() UNIX");
throw bad_function_call();
}
}
void SocketHelper::waitConnection() {
closeClient();
spdlog::debug("Attente de connexion sur la socket ...");
if (this->isInet()) {
waitConnectionInet();
} else if (this->isUnix()) {
waitConnectionUnix();
} else {
spdlog::error("Type de socket non supportée");
throw invalid_argument("Type de socket non supportée. Valid 'unix' et 'inet'");
}
}
void SocketHelper::waitConnectionInet() {
// The accept() call actually accepts an incoming connection
clilen = sizeof(cli_addr_in);
// This accept() function will write the connecting client's address info
// into the the address structure and the size of that structure is clilen.
// The accept() returns a new socket file descriptor for the accepted connection.
// So, the original socket file descriptor can continue to be used
// for accepting new connections while the new socker file descriptor is used for
// communicating with the connected client.
clientSockfd = accept(serverSockfd, (struct sockaddr *) &cli_addr_in, &clilen);
if (clientSockfd < 0) {
spdlog::error("Erreur sur accept() INET");
throw bad_function_call();
}
spdlog::info("Connection INET avec le client {}:{}", inet_ntoa(cli_addr_in.sin_addr), ntohs(cli_addr_in.sin_port));
}
void SocketHelper::waitConnectionUnix() {
// The accept() call actually accepts an incoming connection
clilen = sizeof(cli_addr_un);
// This accept() function will write the connecting client's address info
// into the the address structure and the size of that structure is clilen.
// The accept() returns a new socket file descriptor for the accepted connection.
// So, the original socket file descriptor can continue to be used
// for accepting new connections while the new socker file descriptor is used for
// communicating with the connected client.
clientSockfd = accept(serverSockfd, (struct sockaddr *) &cli_addr_un, &clilen);
if (clientSockfd < 0) {
spdlog::error("Erreur sur accept() UNIX");
throw bad_function_call();
}
spdlog::info("Connection UNIX avec le client sur le fichier {}", socketFile);
}
JsonQuery SocketHelper::getQuery(void) {
std::string data;
while (1) {
char buffer[256];
bzero(buffer, 256);
if (read(clientSockfd, buffer, 255) < 0) {
spdlog::error("Erreur de lecture read()");
break;
}
data+= std::string(buffer);
// on arrete quand on a trouvé \r ou si on a rien lu
if (buffer[0] == 0 || std::find(buffer, buffer + 255, 13) != buffer + 255) {
break;
}
}
JsonQuery q;
if (data.length() == 0) {
spdlog::error("Requête vide du client");
q.action = DATA_INVALID;
} else {
try {
json jsonValue = json::parse(data);
spdlog::trace("Requête client : {}", jsonValue.dump(2));
q.action = jsonValue["action"];
q.data = jsonValue["data"];
} catch (const exception & e) {
spdlog::error("Erreur de lecture du JSON : {}", e.what());
q.action = DATA_UNPARSABLE;
}
}
return q;
}
void SocketHelper::sendResponse(JsonResult response) {
json r;
r["status"] = response.status;
r["action"] = response.action;
r["errorMessage"] = response.errorMessage;
r["data"] = response.data;
spdlog::trace("Réponse client : {}", r.dump(2));
ostringstream outStr;
outStr << r.dump() << endl;
send(clientSockfd, outStr.str().c_str(), outStr.str().length(), 0);
}
void SocketHelper::end() {
closeClient(true);
closeServer();
if (isUnix()) {
removeSocketFile();
}
}
void SocketHelper::closeClient(bool force) {
// Close the socket client if not équals the default value
if (clientSockfd != DEFAULT_SOCKET_FD || force) {
close(clientSockfd);
clientSockfd = DEFAULT_SOCKET_FD;
}
}
void SocketHelper::closeServer() {
close(serverSockfd);
}
void SocketHelper::removeSocketFile() {
// Suppression du fichier si il éxiste (technique rapide POSIX stat)
struct stat buf;
if (stat(socketFile.c_str(), &buf) == 0) {
spdlog::debug("Le fichier socket {} existe, on le supprime", socketFile);
// Suppression du fichier socket
if (remove(socketFile.c_str()) != 0) {
spdlog::error("Suppression du fichier échouée.");
}
}
}