-
Notifications
You must be signed in to change notification settings - Fork 1
/
clientqtwrappers.cpp
403 lines (376 loc) · 15.3 KB
/
clientqtwrappers.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
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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/*------------------------------------------------------------------------------------------------------------------
-- PROGRAM: ChatApp
--
-- FILE: clientCode.c
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 22nd, 2016: Added more functionality
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- FUNCTIONS: void connectToServer(Server IP, callback, callback, callback, nickname, icon)
-- void sendMessage(message to send)
-- char * receiveMessage()
-- void closeConnection()
-- void * receiveThread(void * ptr) (NOTE: Both return and param are unused)
-- void newMessage(char * newpacket)
-- void recvUserLeft(char * newpacket)
-- void recvNewUser(char * newpacket)
-- void disconnectButtonClicked()
--
-- NOTES:
-- The client code was designed to be blackboxed out of all GUI logic. It was designed to be reusable in future
-- applications, and as such is completely isolated from the rest of the application with the exception
-- of the connectToServer, sendMessage and closeConnection calls.
--
-- To update the GUI with received data, the client code utilizes callbacks passed in to the connectToServer function.
-- These function callbacks return void and simply take in a char * of the message received. There are three callbacks,
-- one for receiving a chat message, one for receiving a control message saying a user has connected, and a third for
-- receiving a control message saying a user has disconnected.
--
-- Control messages start with a control flag (DC2 for new user, DC3 for disconnected user), and all information being
-- send is delimited by a DC1 flag.
----------------------------------------------------------------------------------------------------------------------*/
#include "clientqtwrappers.h"
////Variables below are the implementations of the externs////
//Socket variables
char rbuf[BUFLEN];
char username[USERNAMELEN];
int port, sd, icon;
//Thread variables
pthread_t thread;
int threadID;
//Callback declerations
clientCodeCallback recvMessage;
clientCodeCallback newUser;
clientCodeCallback leftUser;
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: connectToServer
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 22nd, 2016: Added more functionality
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void connectToServer(Server's IP address
-- Callback that is invoked when another user sends a message
-- Callback that is invoked when someone new joins the chatroom
-- Callback that is invoked when someone left the chatroom
-- Username we want displayed to other users
-- The index value of the icon we want displayed to other users)
--
-- RETURN: void
--
-- NOTES:
-- Function called to establish a connection to the server. The first parameter is the IP address needed to establish
-- a TCP connection to the server. Next are three callbacks that are invoked by our read thread whenever new information
-- arrises. The first one is called only when someone sends a message over chat, the next when someone connects to the
-- chatroom and the third when someone disconnects. The final two parameters are used for telling the server our nickname
-- to display when we send messages, as well as what icon index we will be using when we type.
--
-- The function will establish a connection to the server, instantiate the proper variables and then start a read
-- thread that will read continously and invoke the callbacks listed when appropriate.
----------------------------------------------------------------------------------------------------------------------*/
void connectToServer(char * serverIP, clientCodeCallback recvCallback, clientCodeCallback newClientCallback, clientCodeCallback leftClientCallback, char * user, int ico) {
struct hostent *hp;
struct sockaddr_in server;
char *host, **pptr;
char str[16], newuser[USERNAMELEN+1];
//Set our connection data & callbacks
host = serverIP;
port = PORTNO;
recvMessage = recvCallback;
newUser = newClientCallback;
leftUser = leftClientCallback;
icon = ico;
strcpy(username, user);
// Create the socket
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Cannot create socket");
return;
}
bzero((char *)&server, sizeof(struct sockaddr_in));
server.sin_family = AF_INET;
server.sin_port = htons(port);
//Get the server address
if ((hp = gethostbyname(host)) == NULL) {
fprintf(stderr, "Unknown server address\n");
return;
}
bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
//Connect to the server
if (connect (sd, (struct sockaddr *)&server, sizeof(server)) == -1) {
fprintf(stderr, "Can't connect to server\n");
perror("connect");
return;
}
//Create our read thread that will read data on the socket
threadID = pthread_create(&thread, NULL, receiveThread, NULL);
//Send a packet to the server stating we are a new user, which will be echo'd to all connected clients
sprintf(newuser, "%c%s%c%lu", NEWUSER, user, MESSAGEDELIMITER, icon);
send (sd, newuser, BUFLEN, 0);
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: sendMessage
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void sendMessage(message to send to the server grabbed from the UI's chat box)
--
-- RETURN: void
--
-- NOTES:
-- Function used to send a message to the server when the user has typed one into the chat box and clicked the send
-- button. It takes the message string, stores it into a buffer along with the username * icon index for displaying
-- purposes, and then writes that to the connected socket.
----------------------------------------------------------------------------------------------------------------------*/
void sendMessage(char * message) {
char sbuf[BUFLEN];
//Format packet
sprintf(sbuf, "%s%c%lu%c%s", username, MESSAGEDELIMITER, icon, MESSAGEDELIMITER, message);
//Send formatted packet through the socket
send (sd, sbuf, BUFLEN, 0);
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: receiveMessage
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: char * receiveMessage()
--
-- RETURN: char * that is the message read from the server
--
-- NOTES:
-- Reads the next packet from the server and returns it as a char * string
----------------------------------------------------------------------------------------------------------------------*/
char * receiveMessage() {
char * bufPointer;
int bytesToRead = BUFLEN;
int bytesRead = 0;
bufPointer = rbuf;
while ((bytesRead = recv (sd, bufPointer, bytesToRead, 0)) < BUFLEN) {
bufPointer += bytesRead;
bytesToRead -= bytesRead;
}
return rbuf;
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: closeConnection
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void closeConnection()
--
-- RETURN: void
--
-- NOTES:
-- Called when we are disconnecting from the server. It first terminates our reading thread, and then closes
-- the socket that is connected to the server.
----------------------------------------------------------------------------------------------------------------------*/
void closeConnection() {
pthread_cancel(thread);
close (sd);
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: receiveThread
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void * receiveThread(a void pointer that is unused)
--
-- RETURN: void * that is unused
--
-- NOTES:
-- After a successful connection has been made to the server, a posix thread is created and given this function as
-- it's starting point. The function loops forever hanging on a receiveMessage call until a packet is received. Once
-- a packet is received, it is then checked to see what type of packet it is. If it starts with a NEWUSER flag (DC2),
-- it is a packet telling us a new user has joined and we pass it over to the appropriate callback. If it starts with
-- a USERLEFT flag (DC3), it is passed over to the appropriate callback. If there is no flag, it is a raw message and
-- passed to the recvMessage callback.
----------------------------------------------------------------------------------------------------------------------*/
void * receiveThread(void * unused) {
char buf[BUFLEN], newbuf[BUFLEN];
while(1) {
//Hang waiting for a packet to be received
strcpy(buf, receiveMessage());
//On successful receive and on valid packet, check what type of packet it is
if (strlen(buf) > 1) {
switch(buf[0]) {
//If it is a packet regarding a new user joining, pass it to the newUser callback
case NEWUSER:
//First, remove the NEWUSER control flag
memmove(newbuf, buf + 1, strlen(buf)-1);
newbuf[strlen(buf)-1] = '\0';
newUser(newbuf);
break;
//If it is a packet regarding a user leaving, pass it to the leftUser callback
case USERLEFT:
//First, remove the USERLEFT control flag
memmove(newbuf, buf + 1, strlen(buf)-1);
newbuf[strlen(buf)-1] = '\0';
leftUser(newbuf);
break;
//If no flag, it is a genuine chat message and is passed to the recvMessage callback as is
default:
recvMessage(buf);
break;
}
}
}
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: newMessage
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void newMessage(new packet read from server)
--
-- RETURN: void
--
-- NOTES:
-- Whenever the posix reading thread reads a packet and deems the packet one of type message, it calls this function
-- to update the UI accordingly. The function takes in a char * of the packet, converts it to a array of QString's
-- and then seperates the contents of the packet by our delimiter character (DC1). Once the contents are layed out,
-- we invoke a method on the main thread that will update the UI accordingly.
----------------------------------------------------------------------------------------------------------------------*/
void newMessage(char * newpacket) {
QStringList packet = QString::fromUtf8(newpacket).split(MESSAGEDELIMITER);
if (packet.length() < 4) {
qDebug() << "Error in newMessage";
return;
}
QString ip = packet.at(0);
QString nickname = packet.at(1);
QString icon = packet.at(2);
QString message = packet.at(3);
QMetaObject::invokeMethod(((QObject*)app), "gotNewMessage", Q_ARG(QString, ip), Q_ARG(QString, nickname), Q_ARG(QString, icon), Q_ARG(QString, message));
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: recvUserLeft
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void recvUserLeft(new packet read from server)
--
-- RETURN: void
--
-- NOTES:
-- Whenever the posix reading thread reads a packet and deems the packet one of type user left chat, it calls this function
-- to update the UI accordingly. The function takes in a char * of the packet, converts it to a array of QString's
-- and then seperates the contents of the packet by our delimiter character (DC1). Once the contents are layed out,
-- we invoke a method on the main thread that will update the UI accordingly.
----------------------------------------------------------------------------------------------------------------------*/
void recvUserLeft(char * newpacket) {
QString ip = QString::fromUtf8(newpacket);
QMetaObject::invokeMethod(((QObject*)app), "gotLostUser", Q_ARG(QString, ip));
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: recvUserLeft
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void recvUserLeft(new packet read from server)
--
-- RETURN: void
--
-- NOTES:
-- Whenever the posix reading thread reads a packet and deems the packet one of type user joined chat, it calls this function
-- to update the UI accordingly. The function takes in a char * of the packet, converts it to a array of QString's
-- and then seperates the contents of the packet by our delimiter character (DC1). Once the contents are layed out,
-- we invoke a method on the main thread that will update the UI accordingly.
----------------------------------------------------------------------------------------------------------------------*/
void recvNewUser(char * newpacket) {
QStringList packet = QString::fromUtf8(newpacket).split(MESSAGEDELIMITER);
if (packet.length() < 3) {
qDebug() << "Error in recv";
for(size_t i; i < packet.length(); i++)
qDebug() << packet.at(i);
return;
}
QString nickname = packet.at(0);
QString icon = packet.at(1);
QString ip = packet.at(2);
QMetaObject::invokeMethod(((QObject*)app), "gotNewUser", Q_ARG(QString, ip), Q_ARG(QString, nickname), Q_ARG(QString, icon));
}
/*------------------------------------------------------------------------------------------------------------------
-- FUNCTION: disconnectButtonClicked
--
-- DATE: March 20th, 2016
--
-- REVISIONS: March 20th, 2016: Created
-- March 23rd, 2016: Commented
--
-- DESIGNER: Carson Roscoe
--
-- PROGRAMMER: Carson Roscoe
--
-- INTERFACE: void disconnectButtonClicked()
--
-- RETURN: void
--
-- NOTES:
-- Invoked when the application clicks the disconnect buttons. Invokes closeConnection which kills the thread and closes
-- the socket.
----------------------------------------------------------------------------------------------------------------------*/
void disconnectButtonClicked() {
closeConnection();
}