diff options
author | Ben Burwell <bburwell1@gmail.com> | 2013-04-29 16:13:34 -0400 |
---|---|---|
committer | Ben Burwell <bburwell1@gmail.com> | 2013-04-29 16:13:34 -0400 |
commit | ca9e46c937e75df43d2f80f0d957fabb07892c29 (patch) | |
tree | 0d8ae2bc1d1e3e248c746586a5daf7cb58a79463 |
Init
-rw-r--r-- | chat_acceptor.h | 136 | ||||
-rw-r--r-- | chat_request.h | 106 | ||||
-rw-r--r-- | check_user_name.h | 27 | ||||
-rw-r--r-- | clean_table.h | 47 | ||||
-rw-r--r-- | loading.h | 24 | ||||
-rw-r--r-- | localchat.c | 102 | ||||
-rw-r--r-- | online.h | 35 | ||||
-rw-r--r-- | own_ip.h | 64 | ||||
-rw-r--r-- | parse.h | 71 | ||||
-rw-r--r-- | peer.h | 13 | ||||
-rw-r--r-- | receive.h | 47 | ||||
-rw-r--r-- | status.h | 69 | ||||
-rw-r--r-- | user_command.h | 62 |
13 files changed, 803 insertions, 0 deletions
diff --git a/chat_acceptor.h b/chat_acceptor.h new file mode 100644 index 0000000..dd94ef5 --- /dev/null +++ b/chat_acceptor.h @@ -0,0 +1,136 @@ +/** + * Localchat + * Ben Burwell + * + * Chat request accepting functionality + */ + +void *receive_chat_messages(void *arg) { + + char in_buf[1024]; + int length = 1024; + int retcode; + + retcode = recv(client_s, in_buf, length, 0); + printf("%s \n", in_buf); + + while (strcmp(in_buf, "SESQ")) { + retcode = recv(client_s, in_buf, length, 0); + if (retcode > 0) { + printf("%s \n", in_buf); + } + } + +} + +void *chat_acceptor(void *arg) { + + int client_s; + char in_buf[4096]; + char out_buf[4096]; + int length = 4096; + char command[4096]; + int retcode; + int i; + char * token; + struct sockaddr_in client_addr; + + client_s = (int) arg; + + retcode = recv(client_s, in_buf, length, 0); + + if (strcmp(in_chat, "Y") == 0) { + + // if already in a chat, reply no and close + strcpy(out_buf, "SESANS:N"); + send(client_s, out_buf, strlen(out_buf)+1, 0); + close(client_s); + + } else { + + // not in a chat, ask the user + + char ans[4]; + printf("\n*** CHAT REQUEST *** \n"); + printf("Accept [y/n]? "); + fflush(stdout); + fgets(ans, 2, stdin); + + if (strcmp(ans, "y") == 0) { + strcpy(out_buf, "SESANS:Y"); + send(client_s, out_buf, strlen(out_buf)+1, 0); + + char inp[256]; + + pthread_t recv_thread; + pthread_create(&recv_thread, NULL, receive_chat_messages, NULL); + + while (strcmp(inp, "/q\n") != 0) { + printf("chat> "); + fgets(inp, 256, stdin); + + if (strcmp(inp, "/q\n") != 0) { + strcpy(out_buf, "SESMSG:"); + strcat(out_buf, inp); + send(client_s, out_buf, strlen(out_buf)+1, 0); + } else { + strcpy(out_buf, "SESQ"); + send(client_s, out_buf, strlen(out_buf)+1, 0); + } + + } + + } else { + strcpy(out_buf, "SESANS:N"); + send(client_s, out_buf, strlen(out_buf)+1, 0); + } + } + + close(client_s); + + return; +} + +void *server_loop(void *arg) { + int server_s; + struct sockaddr_in server_addr; + int client_s; + struct sockaddr_in client_addr; + struct in_addr client_ip_addr; + socklen_t addr_len; + pthread_t thread_id; + int retcode; + + server_s = socket(AF_INET, SOCK_STREAM, 0); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(CHAT_PORT); + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + + retcode = bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (retcode < 0) { + printf("Network error, quitting... \n"); + exit(-1); + } + + listen(server_s, 100); + + while (1) { + addr_len = sizeof(client_addr); + + client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len); + + if (client_s == -1) { + printf("Network error, quitting... \n"); + exit(-1); + } + + pthread_create(&thread_id, NULL, chat_acceptor, (void *)client_s); + } + + return; +} + +void start_accepting_chat_requests() { + pthread_t thread; + pthread_create(&thread, NULL, server_loop, NULL); +}
\ No newline at end of file diff --git a/chat_request.h b/chat_request.h new file mode 100644 index 0000000..8b29886 --- /dev/null +++ b/chat_request.h @@ -0,0 +1,106 @@ +/** + * Localchat + * Ben Burwell + * + * Send chat request + */ + +void send_chat_request(char * username) { + + int i; + char ip[16]; + + pthread_mutex_lock(&peer_table_lock); + + for (i = 0; i < num_peers_in_table; i++) { + if (strcmp(peers[i].username, username) == 0) { + strcpy(ip, peers[i].ip); + } + } + + pthread_mutex_unlock(&peer_table_lock); + + struct sockaddr_in server_addr; + char out_buf[4096]; + char in_buf[4096]; + int retcode; + + // create the socket + client_s = socket(AF_INET, SOCK_STREAM, 0); + + if (client_s < 0) { + printf("Network error, quitting... \n"); + exit(-1); + } + + // fill in server info + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(CHAT_PORT); + server_addr.sin_addr.s_addr = inet_addr(ip); + + retcode = connect(client_s, (struct sockaddr *)&server_addr, sizeof(server_addr)); + + if (retcode < 0) { + printf("Network error, quitting... \n"); + exit(-1); + } + + // send the session request + strcpy(out_buf, "SESREQ"); + send(client_s, out_buf, strlen(out_buf), 0); + + retcode = recv(client_s, in_buf, sizeof(in_buf), 0); + + if (strcmp(in_buf, "SESANS:Y") == 0) { + printf("Request accepted, type /q to leave. \n"); + + // set my in_chat flag + strcpy(in_chat, "Y"); + + // keep looping until no more input + int keep_going = 1; + + while (keep_going) { + + char inp[256]; + printf("chat> "); + fgets(inp, 140, stdin); + + if (strcmp(inp, "/q\n") == 0) { + + // send quit message to peer + strcpy(out_buf, "SESQ"); + send(client_s, out_buf, sizeof(out_buf), 0); + close(client_s); + + // exit the loop + keep_going = 0; + + // print message to user + printf("Session closed. \n"); + + // set my in_chat flag + strcpy(in_chat, "N"); + + } else { + + // send the message + strcpy(out_buf, "SESMSG:"); + strcat(out_buf, inp); + send(client_s, out_buf, sizeof(out_buf), 0); + + // testing... + printf("%s \n", out_buf); + + } + + } + + } else { + printf("Session declined. \n"); + } + + close(client_s); + + return; +}
\ No newline at end of file diff --git a/check_user_name.h b/check_user_name.h new file mode 100644 index 0000000..77051ce --- /dev/null +++ b/check_user_name.h @@ -0,0 +1,27 @@ +/** + * Localchat + * Ben Burwell + * + * Checks whether a username is in use + */ + +int check_user_name(const char * username) { + + int i; + + pthread_mutex_lock(&peer_table_lock); + + for (i = 0; i < num_peers_in_table; i++) { + if (strcmp(peers[i].username, username) == 0) { + + pthread_mutex_unlock(&peer_table_lock); + return 1; + + } + } + + pthread_mutex_unlock(&peer_table_lock); + + return 0; + +}
\ No newline at end of file diff --git a/clean_table.h b/clean_table.h new file mode 100644 index 0000000..d5dc42e --- /dev/null +++ b/clean_table.h @@ -0,0 +1,47 @@ +/** + * Localchat + * Ben Burwell + * + * Table cleaning functions + */ + +void *clean_table(void *arg) { + + int i; + int j; + time_t now; + double diff; + + while (1) { + + pthread_mutex_lock(&peer_table_lock); + + time(&now); + + for (i = 0; i < num_peers_in_table; i++) { + diff = difftime(now, peers[i].last_seen); + if (diff > 10) { + for (j = i; j < num_peers_in_table-1; j++) { + peers[j] = peers[j+1]; + } + num_peers_in_table--; + } + } + + pthread_mutex_unlock(&peer_table_lock); + sleep(1); + } + + return NULL; +} + +void start_clean_table_thread() { + + pthread_t thread; + pthread_mutex_init(&peer_table_lock, NULL); + + if (pthread_create(&thread, NULL, clean_table, NULL) > 0) { + printf("Error starting clean_table thread\n"); + abort(); + } +}
\ No newline at end of file diff --git a/loading.h b/loading.h new file mode 100644 index 0000000..6aa43de --- /dev/null +++ b/loading.h @@ -0,0 +1,24 @@ +/** + * Localchat + * Ben Burwell + * + * Loading display + */ + +void loading() { + + printf(" Loading... 3"); + fflush(stdout); + sleep(1); + + printf("\r Loading... 2"); + fflush(stdout); + sleep(1); + + printf("\r Loading... 1"); + fflush(stdout); + sleep(1); + + printf("\r Loading... Done.\n\n"); + +}
\ No newline at end of file diff --git a/localchat.c b/localchat.c new file mode 100644 index 0000000..60c41d2 --- /dev/null +++ b/localchat.c @@ -0,0 +1,102 @@ +/** + * Localchat + * Ben Burwell + * + * Main Program + */ + +// include necessary libraries +#include <stdio.h> +#include <time.h> +#include <pthread.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <netdb.h> + +// define constants +#define DEBUG 0 +#define MAX_NUM_PEERS 100 +#define CMD_PORT 6060 +#define CHAT_PORT 6061 +#define BROADCAST_IP "192.168.130.255" + +// structs +#include "peer.h" + +// global variables +struct peer peers[MAX_NUM_PEERS]; +int num_peers_in_table = 0; +pthread_mutex_t peer_table_lock; + +char my_ip[64]; +char in_chat[4] = "N"; +const char * username; + +char command[256]; +int client_s; + +// include functions +#include "clean_table.h" +#include "online.h" +#include "own_ip.h" +#include "status.h" +#include "parse.h" +#include "receive.h" +#include "check_user_name.h" +#include "loading.h" +#include "chat_request.h" +#include "chat_acceptor.h" +#include "user_command.h" + +int main(int argc, char const *argv[]) { + + if (argc != 2) { + printf("Usage: %s [username]\n", argv[0]); + return -1; + } + + // figure out IP address + get_own_ip(); + + // start some threads we need + start_receive_thread(); + start_clean_table_thread(); + + // send an ONLINE message + send_online_broadcast(); + + // wait for responses + loading(); + + // check that the username they have requested + // is not already in use + if (check_user_name(argv[1])) { + printf("Sorry, the username you selected is already in use. \n"); + printf("Please choose another. \n\n"); + exit(1); + } + + // now we can set the username field + username = argv[1]; + + // start broadcasting statuses + start_status_thread(); + + // we can accept chats + start_accepting_chat_requests(); + + // enter the user input loop + while (1) { + printf("> "); + fgets(command, 256, stdin); + process_user_command(); + } + + return 0; +}
\ No newline at end of file diff --git a/online.h b/online.h new file mode 100644 index 0000000..d0cbd19 --- /dev/null +++ b/online.h @@ -0,0 +1,35 @@ +/** + * Localchat + * Ben Burwell + * + * Send online broadcast + */ + +void send_online_broadcast() { + int client_sock; // Client socket descriptor + struct sockaddr_in server_addr; // Server Internet address + struct in_addr server_ip_addr; // Server IP Address + int addr_leng; // Internet address length + int retrncode; // Return code + int sOptVal; // Socket option value + int sOptLen; // Socket option length + char out_temp[10]; + + client_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (client_sock < 0) { + exit(-1); + } + + server_addr.sin_family = AF_INET; // Address family to use + server_addr.sin_port = htons(CMD_PORT); // Port num to use + server_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); // IP for Broadcast + sOptVal = 1; + sOptLen = sizeof(int); + setsockopt(client_sock, SOL_SOCKET, SO_BROADCAST, (void*)&sOptVal, sOptLen); + + strcpy(out_temp, "ONLINE"); + retrncode = sendto(client_sock, out_temp, (strlen(out_temp) + 1), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); + + return; +}
\ No newline at end of file diff --git a/own_ip.h b/own_ip.h new file mode 100644 index 0000000..7021c11 --- /dev/null +++ b/own_ip.h @@ -0,0 +1,64 @@ +/** + * Localchat + * Ben Burwell + * + * Gets host IP + */ + +void get_own_ip() { + + char name_buf[256]; + unsigned int buf_len = 256; + struct hostent *host; + int ret_code; + + struct addrinfo hints; + struct addrinfo *result; + struct addrinfo *rptr; // Pointer to linked list. + struct sockaddr_in *ipv4; + void * addr; + char ipstr[64]; + + // Get the name of the host. + ret_code = gethostname(name_buf, buf_len); + if (ret_code < 0) { + exit(1); + } + + if (strcmp(name_buf, "mathcs") == 0) { + + strcpy(my_ip, "192.168.130.10"); + + } else { + + // Get the IP addresses of this host. + memset((void *) &hints, 0, (size_t) sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + ret_code = getaddrinfo(name_buf, NULL, &hints, &result); + if (ret_code != 0) { + perror("Error from getaddrinfo\n"); + exit(1); + } + + rptr = result; + while (rptr != NULL) { + ipv4 = (struct sockaddr_in *) rptr->ai_addr; + addr = &(ipv4->sin_addr); + inet_ntop(rptr->ai_family, addr, ipstr, sizeof(ipstr)); + + rptr = rptr->ai_next; + } + + strcpy(my_ip, ipstr); + + freeaddrinfo(result); // free the linked list + } + + return; +}
\ No newline at end of file @@ -0,0 +1,71 @@ +/** + * Localchat + * Ben Burwell + * + * Parse received command packets + */ + +void parse_command(char packet[256]) { + + char separators[4] = ":"; + char *type; + char *peer_username; + char *peer_ip; + char *peer_in_chat; + int updated_peer; + int i; + + type = strtok(packet, separators); + + if (strcmp(type, "ONLINE") == 0) { + + // send a status broadcast + status_broadcast_once(); + + } else if (strcmp(type, "STATUS") == 0) { + + // lock the peers + pthread_mutex_lock(&peer_table_lock); + + // get username from packet + peer_username = strtok(NULL, separators); + peer_ip = strtok(NULL, separators); + peer_in_chat = strtok(NULL, separators); + + // so we know whether to add a peer later + updated_peer = 0; + + for (i = 0; i < num_peers_in_table; i++) { + if (strcmp(peer_username, peers[i].username) == 0) { + + // found a matching username, update the peer + strcpy(peers[i].in_chat, peer_in_chat); + time( &(peers[i].last_seen) ); + + // we updated the peers, so we won't add it later + updated_peer = 1; + + } + } + + if (!updated_peer) { + // we didn't update a peer, so we need to add it to the table + + strcpy(peers[num_peers_in_table].username, peer_username); + strcpy(peers[num_peers_in_table].ip, peer_ip); + strcpy(peers[num_peers_in_table].in_chat, peer_in_chat); + time( &(peers[num_peers_in_table].last_seen) ); + + num_peers_in_table++; + + //printf("*** %s has come online. \n", peer_username); + + } + + pthread_mutex_unlock(&peer_table_lock); + + } else { + if (DEBUG) printf("Unknown message type: %s \n", type); + } + +}
\ No newline at end of file @@ -0,0 +1,13 @@ +/** + * Localchat + * Ben Burwell + * + * Peer structure + */ + +struct peer { + char username[32]; + char ip[16]; + char in_chat[2]; + time_t last_seen; +};
\ No newline at end of file diff --git a/receive.h b/receive.h new file mode 100644 index 0000000..f89922f --- /dev/null +++ b/receive.h @@ -0,0 +1,47 @@ +/** + * Localchat + * Ben Burwell + * + * Thread management functions + */ + +void *receive_function(void *arg) { + + char in_buf[256]; + struct sockaddr_in server_addr; + int addr_len; + int client_sock = socket(AF_INET, SOCK_DGRAM, 0); + int ret_code; + + if (client_sock < 0) { + exit(-1); + } + + server_addr.sin_family = AF_INET; // Address family to use + server_addr.sin_port = htons(CMD_PORT); // Port num to use + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on any IP address + ret_code = bind(client_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); + + while (1) { + + addr_len = sizeof(server_addr); + ret_code = recvfrom(client_sock, in_buf, sizeof(in_buf), 0, (struct sockaddr *)&server_addr, &addr_len); + + if (DEBUG) printf("Received from server: %s (%d) \n", in_buf, ret_code); + parse_command(in_buf); + } + + return; +} + +void start_receive_thread() { + + pthread_t thread; + + if (pthread_create(&thread, NULL, receive_function, NULL) > 0) { + printf("Error starting receive thread \n"); + exit(-1); + } + + return; +}
\ No newline at end of file diff --git a/status.h b/status.h new file mode 100644 index 0000000..8c3df4b --- /dev/null +++ b/status.h @@ -0,0 +1,69 @@ +/** + * Localchat + * Ben Burwell + * + * Status broadcast thread functionality + */ + +void status_broadcast_once() { + + if (username == NULL) { + return; + } + + int client_sock; // Client socket descriptor + struct sockaddr_in server_addr; // Server Internet address + struct in_addr server_ip_addr; // Server IP Address + int addr_leng; // Internet address length + int retrncode; // Return code + int sOptVal; // Socket option value + int sOptLen; // Socket option length + char out_buf[4096]; // Output buffer for data + char in_buf[4096]; // Input buffer for data + char out_temp[4096]; + + client_sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (client_sock < 0) { + printf("Making socket failure \n"); + exit(-1); + } + + server_addr.sin_family = AF_INET; // Address family to use + server_addr.sin_port = htons(CMD_PORT); // Port num to use + server_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP); // IP for Broadcast + sOptVal = 1; + sOptLen = sizeof(int); + setsockopt(client_sock, SOL_SOCKET, SO_BROADCAST, (void*)&sOptVal, sOptLen); + + strcpy(out_temp, "STATUS:"); + strcat(out_temp, username); + strcat(out_temp, ":"); + strcat(out_temp, my_ip); + strcat(out_temp, ":"); + strcat(out_temp, in_chat); + + retrncode = sendto(client_sock, out_temp, (strlen(out_temp) + 1), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); + + return; +} + +void *status_broadcast(void *arg) { + + while (1) { + status_broadcast_once(); + sleep(5); + } +} + +void start_status_thread() { + + pthread_t thread; + + if (pthread_create(&thread, NULL, status_broadcast, NULL) > 0) { + printf("Error starting receive thread \n"); + exit(-1); + } + + return; +}
\ No newline at end of file diff --git a/user_command.h b/user_command.h new file mode 100644 index 0000000..1c554e6 --- /dev/null +++ b/user_command.h @@ -0,0 +1,62 @@ +/** + * Localchat + * Ben Burwell + * + * Process user input + */ + +void process_user_command() { + + int i; + + if (strcmp(command, "quit\n") == 0) { + exit(0); + } else if (strcmp(command, "who\n") == 0) { + + // print a list of online peers + pthread_mutex_lock(&peer_table_lock); + + printf(" +---------+-----------------+----------------------------------+ \n"); + printf(" | In Chat | IP | Username | \n"); + printf(" +---------+-----------------+----------------------------------+ \n"); + + for (i = 0; i < num_peers_in_table; i++) { + printf(" | %s | %-15s | %-32s | \n", peers[i].in_chat, peers[i].ip, peers[i].username); + } + + printf(" +---------+-----------------+----------------------------------+ \n"); + + pthread_mutex_unlock(&peer_table_lock); + + } else if (strcmp(command, "chat\n") == 0) { + + char user[32]; + int exists; + + printf("user: "); + fgets(user, 32, stdin); + + user[strlen(user) - 1] = 0; + + exists = check_user_name(user); + + if (exists) { + printf("Sending chat request to %s... \n", user); + send_chat_request(user); + } else { + printf("%s does not exist. \n", user); + } + + + } else if (strcmp(command, "help\n") == 0 || strcmp(command, "?\n") == 0) { + + printf("Command Summary: \n"); + printf(" who: display a list of online peers \n"); + printf(" chat: initiate a chat session \n"); + + } else if (strcmp(command, "\n") == 0) { + // no command, do nothing. + } else { + printf("Unrecognized command. Type `help` or `?` for help. \n"); + } +}
\ No newline at end of file |