aboutsummaryrefslogtreecommitdiff
path: root/chatty.h
diff options
context:
space:
mode:
authorRaymaekers Luca <raymaekers.luca@gmail.com>2024-10-31 00:32:07 +0100
committerRaymaekers Luca <raymaekers.luca@gmail.com>2024-11-03 00:58:07 +0100
commitb9aeccef208d6d5b7d40b71886981723f1e14b95 (patch)
tree0312eeeb23f17bd6ba7861c112a382a10207eda9 /chatty.h
parent48733b6acfa27af8e030d9b7abfb9109b1ce89e0 (diff)
Added ID system with 1 and 2-way communication
Each client now has an ID that is permanently stored to ID_FILE location. To implement this each client now uses two connections to the server, one for bidirectional communication and one for unidirectional communication. This makes it easier to not receive unexpected message. Also each client and server now has a Client struct that represents a client and a clientsArena associated with it. Minor changes: - Added logging to LOGFILE, that can be turned with LOGGING macro. - Added more error types - Added error handling on server - Added error messages - Added convenience functions - Added disconnectAndNotify() function for convenience - Use recvTextMessageResult as multiple-value-return-type instead of ** - Separated protocol stuff into protocol.h - Added Result types when wanting to return multiple values - Do not allocate arena's with malloc - Added recvAnyMessageType for receiving messages that do not need to be stored - Add UNIFD and BIFD file descriptors for separating requests chatty.c: - Convert ID to string in screen_home() - Removed the fds global variable - Pass fds to threadReconnect - Implement faster sleep with nanosleep(2) - Close file descriptors when failed so we do not have too many file descriptors open server.c: - Send presence messages on disconnect & connect - renamed i to conn
Diffstat (limited to 'chatty.h')
-rw-r--r--chatty.h220
1 files changed, 50 insertions, 170 deletions
diff --git a/chatty.h b/chatty.h
index 916ae4a..5337656 100644
--- a/chatty.h
+++ b/chatty.h
@@ -1,7 +1,8 @@
-#ifndef CHATTY_IMPL
+#ifndef CHATTY_H
#include <assert.h>
#include <locale.h>
+#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -11,6 +12,7 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <time.h>
+#include <unistd.h>
#include <wchar.h>
typedef uint8_t u8;
@@ -28,13 +30,54 @@ typedef enum {
// port for chatty
#define PORT 9983
+// max number of bytes that can be logged at once
+#define LOGMESSAGE_MAX 2048
+#define LOG_FMT "%H:%M:%S "
+#define LOG_LEN 10
#define Kilobytes(Value) ((Value) * 1024)
#define Megabytes(Value) (Kilobytes(Value) * 1024)
#define Gigabytes(Value) (Megabytes((u64)Value) * 1024)
#define Terabytes(Value) (Gigabytes((u64)Value) * 1024)
#define PAGESIZE 4096
+#define local_persist static
+#define global_variable
+#define internal static
+global_variable s32 logfd;
+
+u32
+wstrlen(u32* str)
+{
+ u32 i = 0;
+ while (str[i] != 0)
+ i++;
+ return i;
+}
+
+void
+loggingf(char* format, ...)
+{
+ char buf[LOGMESSAGE_MAX];
+ va_list args;
+ va_start(args, format);
+
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+
+ int n = 0;
+ while (*(buf + n) != 0) n++;
+
+ u64 t = time(0);
+ u8 timestamp[LOG_LEN];
+ struct tm* ltime = localtime((time_t*)&t);
+ strftime((char*)timestamp, LOG_LEN, LOG_FMT, ltime);
+ write(logfd, timestamp, LOG_LEN - 1);
+
+ write(logfd, buf, n);
+}
+
+// Arena Allocator
struct Arena {
void* addr;
u64 size;
@@ -46,25 +89,20 @@ struct Arena {
#define PushStruct(arena, type) PushArray((arena), (type), 1)
#define PushStructZero(arena, type) PushArrayZero((arena), (type), 1)
-Arena*
-ArenaAlloc(u64 size)
+// Returns arena in case of success, or 0 if it failed to alllocate the memory
+void
+ArenaAlloc(Arena* arena, u64 size)
{
- Arena* arena = (Arena*)malloc(sizeof(Arena));
-
- arena->addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if (arena->addr == MAP_FAILED)
- return NULL;
+ arena->addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ assert(arena->addr != MAP_FAILED);
arena->pos = 0;
arena->size = size;
-
- return arena;
}
void
ArenaRelease(Arena* arena)
{
munmap(arena->addr, arena->size);
- free(arena);
}
void*
@@ -73,167 +111,9 @@ ArenaPush(Arena* arena, u64 size)
u8* mem;
mem = (u8*)arena->addr + arena->pos;
arena->pos += size;
+ assert(arena->pos <= arena->size);
return mem;
}
-/// Protocol
-// - every message has format Header + Message
-// TODO: authentication
-// TODO: encryption
-
-/// Protocol Header
-// - 2 bytes for version
-// - 1 byte for message type
-// - 16 bytes for checksum
-//
-// Text Message
-// - 12 bytes for the author
-// - 8 bytes for the timestamp
-// - 2 bytes for the text length
-// - x*4 bytes for the text
-//
-// History Message
-// This message is for requesting messages sent after a timestamp.
-// - 8 bytes for the timestamp
-
-/// Naming convention
-// Messages end with the Message suffix (eg. TextMessag, HistoryMessage)
-// A function that is coupled to a type works like
-// <noun><type> eg. (printTextMessage, formatTimestamp)
-
-#define PROTOCOL_VERSION 0
-
-typedef struct {
- u16 version;
- u8 type;
-} HeaderMessage;
-
-enum { HEADER_TYPE_TEXT = 0,
- HEADER_TYPE_HISTORY,
- HEADER_TYPE_PRESENCE };
-#define HEADER_TEXTMESSAGE {.version = PROTOCOL_VERSION, .type = HEADER_TYPE_TEXT};
-#define HEADER_HISTORYMESSAGE {.version = PROTOCOL_VERSION, .type = HEADER_TYPE_HISTORY};
-#define HEADER_PRESENCEMESSAGE {.version = PROTOCOL_VERSION, .type = HEADER_TYPE_PRESENCE};
-
-// Size of author string including null terminator
-#define AUTHOR_LEN 13
-// Size of formatted timestamp string including null terminator
-#define TIMESTAMP_LEN 9
-
-typedef struct {
- u8 checksum[16];
- u8 author[AUTHOR_LEN];
- u64 timestamp;
- u16 len; // including null terminator
- u32* text; // placeholder for indexing
- // TODO: 0-length field?
-} TextMessage;
-
-// Size of TextMessage without text pointer, used when receiving the message over a stream
-#define TEXTMESSAGE_TEXT_SIZE(m) (m.len * sizeof(*m.text))
-#define TEXTMESSAGE_SIZE (sizeof(TextMessage) - sizeof(u32*))
-
-typedef struct {
- u64 timestamp;
-} HistoryMessage;
-
-typedef struct {
- u8 author[AUTHOR_LEN];
- u8 type;
-} PresenceMessage;
-enum { PRESENCE_TYPE_CONNECTED = 0,
- PRESENCE_TYPE_DISCONNECTED };
-
-// Returns string for type byte in HeaderMessage
-u8*
-headerTypeString(u8 type)
-{
- switch (type) {
- case HEADER_TYPE_TEXT: return (u8*)"TextMessage";
- case HEADER_TYPE_HISTORY: return (u8*)"HistoryMessage";
- case HEADER_TYPE_PRESENCE: return (u8*)"PresenceMessage";
- default: return (u8*)"Unknown";
- }
-}
-
-u8*
-presenceTypeString(u8 type)
-{
- switch (type) {
- case PRESENCE_TYPE_CONNECTED: return (u8*)"connected";
- case PRESENCE_TYPE_DISCONNECTED: return (u8*)"disconnected";
- default: return (u8*)"Unknown";
- }
-}
-
-// from Tsoding video on minicel (https://youtu.be/HCAgvKQDJng?t=4546)
-// sv(https://github.com/tsoding/sv)
-#define PH_FMT "header: v%d %s(%d)"
-#define PH_ARG(header) header.version, headerTypeString(header.type), header.type
-
-void
-formatTimestamp(u8 tmsp[TIMESTAMP_LEN], u64 t)
-{
- struct tm* ltime;
- ltime = localtime((time_t*)&t);
- strftime((char*)tmsp, TIMESTAMP_LEN, "%H:%M:%S", ltime);
-}
-
-void
-printTextMessage(TextMessage* message, u8 wide)
-{
- u8 timestamp[TIMESTAMP_LEN] = {0};
- formatTimestamp(timestamp, message->timestamp);
-
- assert(setlocale(LC_ALL, "") != NULL);
-
- if (wide)
- wprintf(L"TextMessage: %s [%s] %ls\n", timestamp, message->author, (wchar_t*)&message->text);
- else {
- u8 str[message->len];
- wcstombs((char*)str, (wchar_t*)&message->text, message->len * sizeof(*message->text));
- printf("TextMessage: %s [%s] (%d)%s\n", timestamp, message->author, message->len, str);
- }
-}
-
-// Receive a message from fd and store it to the msgsArena,
-// if dest is not NULL point it to the new message created on msgsArena
-// Returns the number of bytes received
-u32
-recvTextMessage(Arena* msgsArena, u32 fd, TextMessage** dest)
-{
- s32 nrecv = 0;
-
- TextMessage* message = ArenaPush(msgsArena, TEXTMESSAGE_SIZE);
- if (dest != NULL)
- *dest = message;
-
- // Receive everything but the text so we can know the text's size and act accordingly
- nrecv = recv(fd, message, TEXTMESSAGE_SIZE, 0);
- assert(nrecv != -1);
- assert(nrecv == TEXTMESSAGE_SIZE);
-
- nrecv = 0;
-
- // Allocate memory for text and receive in that memory
- u32 text_size = message->len * sizeof(*message->text);
- ArenaPush(msgsArena, text_size);
-
- nrecv = recv(fd, (u8*)&message->text, text_size, 0);
- assert(nrecv != -1);
- assert(nrecv == message->len * sizeof(*message->text));
-
- return TEXTMESSAGE_SIZE + nrecv;
-}
-
-u32
-wstrlen(u32* str)
-{
- u32 i = 0;
- while (str[i] != 0)
- i++;
- return i;
-}
-
#endif
#define CHATTY_H