-
Notifications
You must be signed in to change notification settings - Fork 0
/
chatroom_client.c
executable file
·188 lines (161 loc) · 5.28 KB
/
chatroom_client.c
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
//Team: Nathaniel Leake 424003778, Chase Elander 423005409
//To compile: "make" or "gcc chatroom_client.c";
//Server available at chatroom-438.cf, or you can run your own
#include "interface.h"
#include <netinet/in.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
int connect_to(const char* host, const unsigned short port);
struct Reply process_command(const int sockfd, char* command);
void enter_chatmode(const char* host, const int port);
int hostname_to_ip(char* hostname, char* ip);
int REPLY_SIZE = sizeof(struct Reply);
int main(int argc, char** argv){
//If not specified, use defaults.
char* host_addr = argc > 1 ? argv[1] : "chat-room-438.cf";
char* ip_addr[16];
hostname_to_ip(host_addr, ip_addr);
unsigned short port = argc > 2 ? atoi(argv[2]) : 59254;
// printf("Host = %s, Port = %d\n", host_addr, port);
/* if(argc != 3){
fprintf(stderr, "Usage: Enter host address and port number\n");
fprintf(stderr, "Example: ./a.out chat-room-438.cf 59254\n");
return EXIT_FAILURE;
}*/
// Ping the Chatroom Manager to make sure we can send commands
int sockfd = connect_to(ip_addr, port);
if(sockfd < 0 || write(sockfd, "PING", 4) < 0){
exit(EXIT_FAILURE);
}
display_title();
char command[MAX_DATA];
while(1){
// Read a command from the user
get_command(command, MAX_DATA);
touppercase(command);
// If exiting the program
if(startswith(command, "EXIT")) break;
// Connect to the Chatroom Manager
if((sockfd = connect_to(ip_addr, port)) < 0) exit(EXIT_FAILURE);
// Send command to the Chatroom Manager
struct Reply reply = process_command(sockfd, command);
display_reply(command, reply);
// If a successful JOIN, put this client in chatmode
if(reply.status == SUCCESS && startswith(command, "JOIN")){
enter_chatmode(/*reply.host*/ip_addr, reply.port);
}
}
}
/*
* Connect to the server using given host and port information
* @parameter host host address given by command line argument
* @parameter port port given by command line argument
* @return socket fildescriptor
*/
int connect_to(const char* host, const unsigned short port){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0){
perror("Error getting socket");
return sockfd;
}
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(host);
serv_addr.sin_port = htons(port);
if(connect(sockfd, (struct sockaddr*) &serv_addr , sizeof(serv_addr)) < 0){
perror("Error connecting to server");
return -1;
}
return sockfd;
}
/*
* Send an input command to the server and return the result
* @parameter sockfd socket file descriptor to access the server
* @parameter command command that will be sent to the server
* @return Reply
*/
struct Reply process_command(const int sockfd, char* command){
// If reply status is never set, assume FAILURE_UNKNOWN
struct Reply reply;
reply.status = FAILURE_UNKNOWN;
// Write command to server
if(write(sockfd, command, MAX_DATA) < 0){
perror("Unable to send message to server");
return reply;
}
// Receive reply from server with 10 second timeout
struct timeval tv = {10, 0};
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if(read(sockfd, &reply, REPLY_SIZE) <= 0){
perror("Missing/Invalid response from server");
}
return reply;
}
void* input_listener_worker(int chat_server){
// Separate thread for reading from IO and sending to server
char message[MAX_DATA];
do{
get_message(message, MAX_DATA-1);
// Gracefully exit if the server decides to kick this client
if(startswith(message, "QUIT")) break;
// Add a newline character to the end of this message
message[strlen(message)+1] = '\0';
message[strlen(message)] = '\n';
} while(write(chat_server, message, MAX_DATA) >= 0);
if(!startswith(message, "QUIT")){
// If we get here, there was an error in write()
printf("Unable to send message to server\n");
}
close(chat_server);
chat_server = 0;
return NULL;
}
/*
* Get into the chat mode
* @parameter host host address
* @parameter port port
*/
void enter_chatmode(const char* host, const int port){
int chat_server = connect_to(host, port);
if(chat_server < 0) return;
printf("Entering ChatMode\n");
char message[MAX_DATA];
// Input a username and send it to the server
if(USE_USERNAMES){
printf("Please enter a username: \n");
get_message(message, MAX_DATA);
if(write(chat_server, message, MAX_DATA) < 0){
perror("Unable to send message to server");
return;
}
}
pthread_t tid;
pthread_create(&tid, NULL, &input_listener_worker, chat_server);
// (Parent) Process to display messages from server
while(read(chat_server, message, MAX_DATA) > 0 && chat_server){
// if(startswith(message, "QUIT")) break;
display_message(message);
}
// if(startswith(message, "QUIT")) printf("Kicked from server :O\n");
// else printf("Connection closed\n");
// Close the pthread if it's still going
pthread_cancel(tid);
close(chat_server);
printf("Exiting ChatMode\n");
}
int hostname_to_ip(char* hostname, char* ip){
struct hostent* he;
struct in_addr** addr_list;
int i;
if((he = gethostbyname(hostname))){
addr_list = (struct in_addr**) he->h_addr_list;
for(i=0; addr_list[i]; ++i){
// Return the file one
strcpy(ip, inet_ntoa(*addr_list[i]));
return EXIT_SUCCESS;
}
}
herror("gethostbyname");
return EXIT_FAILURE;
}