Skip to content

Commit

Permalink
Latest commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nick-botticelli committed Jul 16, 2024
1 parent f7ea7a4 commit ad588fc
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 155 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/make.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Build with GNU Make

on: [push, pull_request]

jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04]

steps:
- uses: actions/[email protected]
- name: Make program
run: make
8 changes: 2 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Many thanks to Job Vranish - see https://spin.atomicobject.com/2016/08/26/makefile-c-projects/
TARGET_EXEC := chat_node
TARGET_EXEC := chatroom-c
BUILD_DIR := ./build
SRC_DIRS := ./src
SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s)
Expand All @@ -10,10 +10,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS))
CPPFLAGS := $(INC_FLAGS) -MMD -MP -Wall -Wno-unused-command-line-argument -Wno-undefined-inline
LDFLAGS := -pthread -lpthread

RELEASE_FLAGS := -Ofast -DNDEBUG -fvisibility=hidden -fstack-protector-strong \
-fomit-frame-pointer -fPIE -fstack-clash-protection -fsanitize=bounds \
-fsanitize-undefined-trap-on-error -D_FORTIFY_SOURCE=3 -flto
# RELEASE_LDFLAGS := -Wl,-z,relro,-z,now,-z,noexecstack,-z,separate-code
RELEASE_FLAGS := -O3 -DNDEBUG -fvisibility=hidden -fomit-frame-pointer

DEBUG_FLAGS := -O0 -g3 -fno-omit-frame-pointer -fno-common -fno-optimize-sibling-calls
ASAN_FLAGS := $(DEBUG_FLAGS) -fsanitize=address,undefined
Expand All @@ -23,7 +20,6 @@ TSAN_FLAGS := $(DEBUG_FLAGS) -fsanitize=thread
.PHONY: all debug asan msan tsan clean

all: CPPFLAGS += $(RELEASE_FLAGS)
# all: LDFLAGS += $(RELEASE_LDFLAGS)
all: $(BUILD_DIR)/$(TARGET_EXEC)
strip $(BUILD_DIR)/$(TARGET_EXEC)

Expand Down
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
# ProgAssignment-5
Mesh chat application
# chatroom-c
chatroom-c is a multi-threaded CLI-based chat application in a mesh network design using plain C
with pthreads + sockets.

# Authors
* Mahafuj Alam ([email protected])
* Nicholas Botticelli ([email protected])
# Building
* GNU Make:
`make`

# Usage
Example:
`./chat_node`
`./chatroom-c`

Must have a `chatnode.properties` file matching the format shown in the repository. Default port is
`51966`, aka `0xCAFE`. Comment or remove the `ip` key in the properties file to host the chat room
on the desired port, otherwise, the IP + port combo is used by clients to connect to an existing
chat room by filling in the properties file correctly and running `/join` once the client is
started.

# Authors
* Mahafuj Alam
* Nicholas Botticelli

# Demo
An example video showing cross-network chatting using an older version with a couple visual bugs
(not affecting internal logic) can be seen in the YouTube video below.

[![Video demo](https://img.youtube.com/vi/PTEXrJTe-WI/0.jpg)](https://www.youtube.com/watch?v=PTEXrJTe-WI)
2 changes: 1 addition & 1 deletion chatnode.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ip = 127.0.0.1
port = 51966
username = Person3
username = Person2
35 changes: 4 additions & 31 deletions src/chat_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,7 @@
#include "debug.h"
#include "main.h"

inline void printNodeList(Node *nodeList) {
#ifndef NDEBUG
Node *curNode = nodeList;

// Traverse linked list until we find the node to remove
printf("===== Node list =====\n");
while (curNode != NULL) {
printf("%p: %s:%d - %s -\t-> %p\n", curNode, curNode->ip, curNode->port, curNode->username, curNode->nextNode);
curNode = curNode->nextNode;
}
printf("=====================\n");
#endif
}

inline Node *createNode(char *ip, short port, char *username, bool createSocket, bool initialNode) {
Node *createNode(char *ip, short port, char *username, bool createSocket, bool initialNode) {
Node *node = malloc(sizeof(Node));
node->ip = ip;
node->port = port;
Expand All @@ -36,7 +22,7 @@ inline Node *createNode(char *ip, short port, char *username, bool createSocket,
return node;
}

inline Node *acceptNode(Node **nodeList) {
Node *acceptNode(Node **nodeList) {
struct sockaddr addr;
socklen_t addrlen = sizeof(addr);
int sock = accept((*nodeList)->sock, &addr, &addrlen);
Expand All @@ -56,7 +42,7 @@ inline Node *acceptNode(Node **nodeList) {
return node;
}

inline void addNode(Node **nodeList, Node *node) {
void addNode(Node **nodeList, Node *node) {
if (*nodeList == NULL) { // TODO
*nodeList = node;
// node->nextNode = NULL;
Expand All @@ -81,7 +67,7 @@ inline void addNode(Node **nodeList, Node *node) {
printNodeList(*nodeList);
}

inline void removeNode(Node **nodeList, Node *node) {
void removeNode(Node **nodeList, Node *node) {
Node *prevNode = NULL;
Node *curNode = *nodeList;

Expand Down Expand Up @@ -112,23 +98,10 @@ inline void removeNode(Node **nodeList, Node *node) {
// nodeList->node = node;
*nodeList = node;

// if (curNode == *nodeList) {
// // Linked list is empty
// (*nodeList)->nextNode = NULL;
// debug("Removing only node in the node list!");
// }
// else {
// // First item is the node to remove (curNode)
// (*nodeList)->nextNode = curNode->nextNode;
// debug("Removing first node in node list!");
// }

debug("Removing the head in node list!");
*nodeList = curNode->nextNode;
}

printf("%s has left the chat room.\n", curNode->username);

// Clean up old node
curNode->connected = false;
free(curNode->ip);
Expand Down
10 changes: 5 additions & 5 deletions src/chat_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ typedef struct {
* @param createSocket whether to create a new socket immediately or leave null
* @param initialNode whether the created node is the initial host node
*/
extern inline Node *createNode(char *ip, short port, char *username, bool createSocket, bool initialNode);
extern Node *createNode(char *ip, short port, char *username, bool createSocket, bool initialNode);

extern inline void printNodeList(Node *nodeList);
extern inline Node *acceptNode(Node **nodeList);
extern inline void addNode(Node **nodeList, Node *node);
extern inline void removeNode(Node **nodeList, Node *node);
extern void printNodeList(Node *nodeList);
extern Node *acceptNode(Node **nodeList);
extern void addNode(Node **nodeList, Node *node);
extern void removeNode(Node **nodeList, Node *node);
45 changes: 44 additions & 1 deletion src/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include <stdlib.h>

#include "main.h"

void debug_hexdump(void *data, size_t size) {
#ifndef NDEBUG
Expand Down Expand Up @@ -41,3 +40,47 @@ void debug_hexdump(void *data, size_t size) {
}
#endif
}

void printNodeList(Node *nodeList) {
#ifndef NDEBUG
Node *curNode = nodeList;

// Traverse linked list until we find the node to remove
printf("===== Node list =====\n");
while (curNode != NULL) {
printf("%p: %s:%d - %s -\t-> %p\n", curNode, curNode->ip, curNode->port, curNode->username, curNode->nextNode);
curNode = curNode->nextNode;
}
printf("=====================\n");
#endif
}

void debugMessage(Message message) {
#ifndef NDEBUG
MessageType messageType = getMessageType(message.header);

printf("Message {\n");
printf("\theader:\n");
printf("\t\ttype = %d\n", messageType);

switch (messageType) {
case MSG_JOIN:
printf("\tusername = %s\n", message.username);
printf("\tport = %d\n", message.port);
break;
case MSG_WELCOME:
printf("\tremoteUsername = %s\n", message.remoteUsername);
case MSG_ADD_MEMBER:
printf("\tWIP\n");
break;
case MSG_NOTE:
printf("\tnote = %s\n", message.note);
break;
case MSG_LEAVE:
printf("\tshutdownAll = %s\n", getBit(message.header, 0) ? "true" : "false");
break;
}

printf("}\n");
#endif
}
7 changes: 7 additions & 0 deletions src/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include <errno.h>
#include <string.h>

#include "chat_node.h"
#include "message.h"

#ifndef NDEBUG
// debug() - maskable debug message, expanded only if symbol DEBUG is defined
#define debug(M, ...) fprintf(stderr, "[DEBUG] %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
Expand All @@ -34,3 +37,7 @@


void debug_hexdump(void *data, size_t size);

void printNodeList(Node *nodeList);

void debugMessage(Message message);
63 changes: 22 additions & 41 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,8 @@
#include "receiver_handler.h"
#include "sender_handler.h"

// TODO: Move this to debug.c
extern inline void debugMessage(Message message) {
#ifndef NDEBUG
MessageType messageType = getMessageType(message.header);

printf("Message {\n");
printf("\theader:\n");
printf("\t\ttype = %d\n", messageType);

switch (messageType) {
case MSG_JOIN:
printf("\tusername = %s\n", message.username);
printf("\tport = %d\n", message.port);
break;
case MSG_WELCOME:
printf("\tremoteUsername = %s\n", message.remoteUsername);
case MSG_ADD_MEMBER:
printf("\tWIP\n");
break;
case MSG_NOTE:
printf("\tnote = %s\n", message.note);
break;
case MSG_LEAVE:
printf("\tshutdownAll = %s\n", getBit(message.header, 0) ? "true" : "false");
break;
}

printf("}\n");
#endif
}

inline Node *loadProperties() {
Node *loadProperties() {
Properties *properties = property_read_properties(CONFIG_PATH);

char *ip = property_get_property(properties, "ip");
Expand Down Expand Up @@ -84,6 +54,7 @@ void *send_handler(void *nodeListRaw) {

if (cmdResult.action == ACTION_LEAVE) {
// Disconnect from all other nodes
debug("Disconnecting from all other nodes...");
Node *curNode = nodeList->initialNode ? nodeList->nextNode->nextNode : nodeList->nextNode;
while (curNode != NULL) {
curNode->connected = false;
Expand Down Expand Up @@ -134,8 +105,14 @@ void *receive_handler(void *recHandlerDataRaw) {
}

if (receiveMessage(nodeList, node, &message)) {
if (!handleClient(nodeList, node, message)) {
exit(EXIT_SUCCESS); // Quick and dirty
bool clientStatus = handleClient(nodeList, node, message);

// // Loop through disconnected nodes and remove from list

if (!clientStatus) {
debug("Shutting down client.");
exit(EXIT_SUCCESS); // TODO: Quick and dirty
// break;
}
}
}
Expand All @@ -152,7 +129,7 @@ void *initial_receive_handler(void *nodeRaw) {

// Create unnamed network socket for server to listen on
if ((node->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Error creating initial server socket");
perror("Error creating initial server socket!");
exit(EXIT_FAILURE);
}

Expand All @@ -164,7 +141,7 @@ void *initial_receive_handler(void *nodeRaw) {

// Bind socket to a port
if (bind(node->sock, (struct sockaddr *) &server_address, sizeof(server_address)) == -1) {
perror("Error binding initial server socket");
perror("Error binding initial server socket!");
exit(EXIT_FAILURE);
}

Expand All @@ -179,7 +156,7 @@ void *initial_receive_handler(void *nodeRaw) {

// Listen for client connections (pending connections get put into a queue)
if (listen(node->sock, 1) == -1) {
perror("Error listening on initial server socket");
perror("Error listening on initial server socket!");
exit(EXIT_FAILURE);
}

Expand All @@ -192,14 +169,18 @@ void *initial_receive_handler(void *nodeRaw) {

pthread_t receive_thread;
if (pthread_create(&receive_thread, NULL, receive_handler, (void *) recHandlerData) == -1) {
perror("Error: Could not create receiver thread");
perror("Error: Could not create receiver thread!");
exit(EXIT_FAILURE);
}

// detach the thread so that we don't have to wait (join) with it to reclaim memory.
// Detach the thread so that we don't have to wait (join) with it to reclaim memory.
// memory will be reclaimed when the thread finishes.
if (pthread_detach(receive_thread) == -1) {
perror("Error: Could not detach receiver thread");
int pthreadDetachCode = pthread_detach(receive_thread);

// free(recHandlerData); // TODO: Verify?

if (pthreadDetachCode == -1) {
perror("Error: Could not detach receiver thread!");
exit(EXIT_FAILURE);
}
}
Expand All @@ -211,7 +192,7 @@ int main(int argc, char *argv[]) {
int result = EXIT_SUCCESS;
pthread_t senderThread, initialReceiverThread;

puts("=== chat_node ===\n");
puts("=== chatroom-c ===\n");

Node *nodeList;
Node *node = loadProperties();
Expand Down
5 changes: 1 addition & 4 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
#include "chat_node.h"
#include "properties.h"

// TODO: Remove
#define SERVER_ADDR "127.0.0.1"

#define CONFIG_PATH "chatnode.properties"

extern inline Node *loadProperties();
extern Node *loadProperties();
extern void *receive_handler(void *recHandlerDataRaw);
Loading

0 comments on commit ad588fc

Please sign in to comment.