diff options
-rwxr-xr-x | build.sh | 6 | ||||
-rw-r--r-- | v1/.gitignore (renamed from .gitignore) | 3 | ||||
-rw-r--r-- | v1/README.md (renamed from README.md) | 5 | ||||
-rw-r--r-- | v1/arena.h | 116 | ||||
-rwxr-xr-x | v1/build.sh | 6 | ||||
-rw-r--r-- | v1/client.c (renamed from client.c) | 58 | ||||
-rw-r--r-- | v1/common.h (renamed from common.h) | 74 | ||||
-rw-r--r-- | v1/compile_flags.txt (renamed from compile_flags.txt) | 0 | ||||
-rw-r--r-- | v1/recv.c (renamed from recv.c) | 24 | ||||
-rw-r--r-- | v1/send.c (renamed from send.c) | 28 | ||||
-rw-r--r-- | v1/server.c (renamed from server.c) | 11 | ||||
-rw-r--r-- | v1/termbox2.h (renamed from termbox2.h) | 0 |
12 files changed, 229 insertions, 102 deletions
diff --git a/build.sh b/build.sh deleted file mode 100755 index 39e3f42..0000000 --- a/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -set -x -gcc -g -Wall -pedantic -std=c99 -o chatty client.c -gcc -g -Wall -pedantic -std=c99 -o server server.c -gcc -g -Wall -pedantic -std=c99 -o send send.c -gcc -g -Wall -pedantic -std=c99 -o recv recv.c diff --git a/.gitignore b/v1/.gitignore index fd52ccf..a9ac961 100644 --- a/.gitignore +++ b/v1/.gitignore @@ -2,5 +2,6 @@ chatty server recv send +client + tags -tmp @@ -15,6 +15,7 @@ The idea is the following: # Server - min height & width - wrapping input +- [ ] bug: retransmissed message have no text - [ ] history - [x] max y for new messages and make them scroll - [x] check resize event @@ -31,13 +32,17 @@ The idea is the following: - bug: when having multiple messages and resizing a lot, the output will be in shambles - bug: when resizing afters sending messages over network it crashes - bug: all messages using the same buffer for text +- bug: 1. open chatty, send a message with send, messages won't be received by the server +- bug: memcpy is overlapping a byte in the next message when messages_add - use pointer for add_message - validation of sent/received messages - handle disconnection # Questions - will two consecutive sends be read in one recv + - not always. - can you recv a message in two messages + - yes, done. # Message protocol Version 1 diff --git a/v1/arena.h b/v1/arena.h new file mode 100644 index 0000000..6f371f4 --- /dev/null +++ b/v1/arena.h @@ -0,0 +1,116 @@ +#ifndef ARENA_IMPL +#define ARENA_IMPL + +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/mman.h> +#include <unistd.h> + +#define PAGESIZE 4096 + +#ifndef ARENA_MEMORY +#define ARENA_MEMORY PAGESIZE +#endif + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +struct Arena { + void *memory; + u64 size; + u64 pos; +} typedef Arena; + +// Create an arena +Arena *ArenaAlloc(void); +// Destroy an arena +void ArenaRelease(Arena *arena); + +// Push bytes on to the arena | allocating +void *ArenaPush(Arena *arena, u64 size); +void *ArenaPushZero(Arena *arena, u64 size); + +#define PushArray(arena, type, count) (type *)ArenaPush((arena), sizeof(type) * (count)) +#define PushArrayZero(arena, type, count) (type *)ArenaPushZero((arena), sizeof(type) * (count)) +#define PushStruct(arena, type) PushArray((arena), (type), 1) +#define PushStructZero(arena, type) PushArrayZero((arena), (type), 1) + +// Free some bytes by popping the stack +void ArenaPop(Arena *arena, u64 size); +// Get the number of bytes allocated +u64 ArenaGetPos(Arena *arena); + +void ArenaSetPosBack(Arena *arena, u64 pos); +void ArenaClear(Arena *arena); + +Arena *ArenaAlloc(void) +{ + // NOTE: If the arena is created here the pointer to the memory get's overwritten with size in + // ArenaPush, so we are forced to use malloc + Arena *arena = malloc(sizeof(Arena)); + + arena->memory = mmap(NULL, ARENA_MEMORY, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (arena->memory == MAP_FAILED) + return NULL; + + arena->pos = 0; + arena->size = ARENA_MEMORY; + + return arena; +} + +void ArenaRelease(Arena *arena) +{ + munmap(arena->memory, ARENA_MEMORY); + free(arena); +} + +void *ArenaPush(Arena *arena, u64 size) +{ + u64 *mem; + mem = (u64 *)arena->memory + arena->pos; + arena->pos += size; + return mem; +} + +void *ArenaPushZero(Arena *arena, u64 size) +{ + u64 *mem; + mem = (u64 *)arena->memory + arena->pos; + bzero(mem, size); + arena->pos += size; + return mem; +} + +void ArenaPop(Arena *arena, u64 size) +{ + arena->pos -= size; +} + +u64 ArenaGetPos(Arena *arena) +{ + return arena->pos; +} + +void ArenaSetPosBack(Arena *arena, u64 pos) +{ + arena->pos -= pos; +} + +void ArenaClear(Arena *arena) +{ + bzero(arena->memory, arena->size); + arena->pos = 0; +} + +#endif diff --git a/v1/build.sh b/v1/build.sh new file mode 100755 index 0000000..a80b193 --- /dev/null +++ b/v1/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -x +gcc -ggdb -Wall -pedantic -std=c99 -o client client.c +gcc -ggdb -Wall -pedantic -std=c99 -o server server.c +gcc -ggdb -Wall -pedantic -std=c99 -o send send.c +gcc -ggdb -Wall -pedantic -std=c99 -o recv recv.c @@ -29,17 +29,17 @@ int prompt_offs_y = 3; // filedescriptor for server static int serverfd; // Input message to be send -struct message input = { +Message input = { .author = USERNAME, .timestamp = {0}, - .len = 0, + .text_len = 0, }; // current amount of messages int nmessages = 0; // length of messages array int messages_size = MESSAGES_SIZE; // All messages sent and received in order -struct message messages[MESSAGES_SIZE] = {0}; +Message messages[MESSAGES_SIZE] = {0}; // incremented each time a new message is printed int msg_y = 0; @@ -51,7 +51,7 @@ void err_exit(const char *msg); void scren_welcome(void); // Append msg to the messages array. Returns -1 if there was no space in the messages array // otherwise returns 0 on success. -u8 message_add(struct message msg); +u8 messages_add(Message *msg); void cleanup(void) { @@ -84,22 +84,16 @@ void screen_welcome(void) } } -u8 message_add(struct message msg) +u8 messages_add(Message *msg) { if (nmessages == messages_size) { return -1; } - int i; - messages[nmessages].text = input.text; - ; - messages[nmessages].text[input.len] = 0; - messages[nmessages].len = input.len; - for (i = 0; (messages[nmessages].timestamp[i] = msg.timestamp[i]); i++) - ; - messages[nmessages].timestamp[i] = 0; - for (i = 0; (messages[nmessages].author[i] = msg.author[i]); i++) - ; + memcpy(messages[nmessages].author, msg->author, MESSAGE_AUTHOR_LEN); + memcpy(messages[nmessages].timestamp, msg->timestamp, MESSAGE_TIMESTAMP_LEN); + messages[nmessages].text_len = msg->text_len; + messages[nmessages].text = msg->text; nmessages++; msg_y++; @@ -119,7 +113,7 @@ int main(void) input.text = buf; int serverfd, ttyfd, resizefd; - struct message msg_recv = {0}; + Message msg_recv = {0}; const struct sockaddr_in address = { AF_INET, htons(PORT), @@ -173,11 +167,11 @@ int main(void) tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); } tb_set_cursor(curs_offs_x, global.cursor_y); - input.len = 0; + input.text_len = 0; break; // send message case TB_KEY_CTRL_M: - if (input.len <= 0) + if (input.text_len <= 0) break; while (global.cursor_x > curs_offs_x) { global.cursor_x--; @@ -186,20 +180,20 @@ int main(void) tb_set_cursor(curs_offs_x, global.cursor_y); // zero terminate - input.text[input.len] = 0; + input.text[input.text_len] = 0; // print new message time(&now); ltime = localtime(&now); - strftime(input.timestamp, sizeof(input.timestamp), "%H:%M:%S", ltime); + strftime((char*)input.timestamp, sizeof(input.timestamp), "%H:%M:%S", ltime); - message_add(input); + messages_add(&input); - if (send(serverfd, &input, sizeof(input), 0) == -1) + if (message_send(&input, serverfd) == -1) err_exit("Error while sending message."); // reset buffer - input.len = 0; + input.text_len = 0; // update the screen // NOTE: kind of wasteful cause we should only display new message @@ -210,32 +204,32 @@ int main(void) // remove word case TB_KEY_CTRL_W: // Delete consecutive space - while (input.text[input.len - 1] == ' ' && global.cursor_x > curs_offs_x) { + while (input.text[input.text_len - 1] == ' ' && global.cursor_x > curs_offs_x) { global.cursor_x--; - input.len--; + input.text_len--; tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); } // Delete until next non-space - while (input.text[input.len - 1] != ' ' && global.cursor_x > curs_offs_x) { + while (input.text[input.text_len - 1] != ' ' && global.cursor_x > curs_offs_x) { global.cursor_x--; - input.len--; + input.text_len--; tb_print(global.cursor_x, global.cursor_y, 0, 0, " "); } - input.text[input.len] = 0; + input.text[input.text_len] = 0; break; } // append pressed character to input.text // TODO: wrap instead, allocate more ram for the message instead - if (ev.ch > 0 && input.len < MESSAGE_MAX && input.len < global.width - 3 - 1) { + if (ev.ch > 0 && input.text_len < MESSAGE_MAX && input.text_len < global.width - 3 - 1) { tb_printf(global.cursor_x, global.cursor_y, 0, 0, "%c", ev.ch); global.cursor_x++; - input.text[input.len++] = ev.ch; + input.text[input.text_len++] = ev.ch; } } else if (fds[FD_SERVER].revents & POLLIN) { - int nrecv = recv(serverfd, &msg_recv, sizeof(struct message), 0); + int nrecv = message_receive(&msg_recv, serverfd); if (nrecv == 0) { // Server closed @@ -244,7 +238,7 @@ int main(void) } else if (nrecv == -1) { err_exit("Error while receiveiving from server."); } - message_add(msg_recv); + messages_add(&msg_recv); tb_clear(); screen_welcome(); @@ -12,7 +12,7 @@ // max size for a message sent #define MESSAGE_MAX 256 // max length of author field -#define MESSAGE_AUTHOR_LEN 12 +#define MESSAGE_AUTHOR_LEN 13 // max length of timestamp field #define MESSAGE_TIMESTAMP_LEN 9 // current user's name @@ -21,36 +21,41 @@ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; // To serialize the text that could be arbitrary length the lenght is encoded after the author // string and before the text. -struct message { - char author[MESSAGE_AUTHOR_LEN]; - char timestamp[MESSAGE_TIMESTAMP_LEN]; // HH:MM:SS - u16 len; // length of the text including null terminator +struct Message { + u8 author[MESSAGE_AUTHOR_LEN]; + u8 timestamp[MESSAGE_TIMESTAMP_LEN]; // HH:MM:SS + u16 text_len; // length of the text including null terminator char *text; -}; +} typedef Message; // printf without buffering using write syscall, works when using sockets void writef(char *format, ...); u16 str_len(char *str); -void str_cpy(char *to, char *from); // save the message msg to file in binary format, returns zero on success, returns 1 if the msg.text // was empty which should not be allowed. -u8 save_message(struct message *msg, FILE *f); +u8 message_fsave(Message *msg, FILE *f); // load the message msg from file f, returns zero on success, returns 1 if the msg.text // was empty which should not be allowed. -u8 load_message(struct message *msg, FILE *f); +u8 message_fload(Message *msg, FILE *f); -// Send a stream of bytes containing msg +// Encode msg and send it to fd // return -1 if send() returns -1. Otherwise returns number of bytes sent. -u32 send_message(struct message msg, u32 serverfd); -// Receives a stream of bytes and populates msg with the data received +// NOTE: this function should not alter the content stored in msg. +u32 message_send(Message *msg, u32 fd); +// Decode data from fd and populate msg with it // if recv() returns 0 or -1 it will return early and return 0 or -1 accordingly. // Otherwise returns the number of bytes received -u32 receive_message(struct message *msg, u32 clientfd); +u32 message_receive(Message *msg, u32 fd); void writef(char *format, ...) { @@ -88,22 +93,22 @@ void str_cpy(char *to, char *from) // Save msg to file f // Returns 0 on success, returns 1 if msg->text is NULL, returns 2 if mfg->len is 0 -u8 save_message(struct message *msg, FILE *f) +u8 message_fsave(Message *msg, FILE *f) { if (msg->text == NULL) { return 1; - } else if (msg->len == 0) + } else if (msg->text_len == 0) return 2; fwrite(&msg->timestamp, sizeof(*msg->timestamp) * MESSAGE_TIMESTAMP_LEN, 1, f); fwrite(&msg->author, sizeof(*msg->author) * MESSAGE_AUTHOR_LEN, 1, f); - fwrite(&msg->len, sizeof(msg->len), 1, f); - fputs(msg->text, f); + fwrite(&msg->text_len, sizeof(msg->text_len), 1, f); + fwrite(&msg->text, msg->text_len, 1, f); return 0; } -u8 load_message(struct message *msg, FILE *f) +u8 message_fload(Message *msg, FILE *f) { fread(msg, sizeof(*msg->timestamp) * MESSAGE_TIMESTAMP_LEN + sizeof(*msg->author) * MESSAGE_AUTHOR_LEN, 1, f); u16 len; @@ -111,47 +116,47 @@ u8 load_message(struct message *msg, FILE *f) if (len == 0) { // TODO: Error: empty message should not be allowed // empty message - msg->text = ""; + msg->text = NULL; return 1; } char txt[len]; fgets(txt, len, f); - msg->text = txt; + memcpy(msg->text, txt, len); return 0; } -u32 send_message(struct message msg, u32 serverfd) +u32 message_send(Message *msg, u32 serverfd) { - // stream length : message author : message timestamp : message text + \0 - u32 buf_len = sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN + msg.len; + // for protocol see README.md + u32 buf_len = sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN + msg->text_len; char buf[buf_len]; u32 offset; memcpy(buf, &buf_len, sizeof(buf_len)); offset = sizeof(buf_len); - memcpy(buf + offset, msg.author, MESSAGE_AUTHOR_LEN); + memcpy(buf + offset, msg->author, MESSAGE_AUTHOR_LEN); offset += MESSAGE_AUTHOR_LEN; - memcpy(buf + offset, msg.timestamp, MESSAGE_TIMESTAMP_LEN); + memcpy(buf + offset, msg->timestamp, MESSAGE_TIMESTAMP_LEN); offset += MESSAGE_TIMESTAMP_LEN; - memcpy(buf + offset, msg.text, msg.len); + memcpy(buf + offset, msg->text, msg->text_len); u32 n = send(serverfd, &buf, buf_len, 0); if (n == -1) return n; - writef("%d bytes sent.\n", n); return n; } -u32 receive_message(struct message *msg, u32 clientfd) +u32 message_receive(Message *msg, u32 clientfd) { + // for protocol see README.md // must all be of the s - u32 nrecv, buf_len; + u32 nrecv = 0, buf_len = 0; // limit on what can be received with recv() u32 buf_size = 20; // temporary buffer to receive message data over a stream - char recv_buf[BUF_MAX]; + char recv_buf[BUF_MAX] = {0}; nrecv = recv(clientfd, recv_buf, buf_size, 0); if (nrecv == 0 || nrecv == -1) @@ -168,12 +173,9 @@ u32 receive_message(struct message *msg, u32 clientfd) nrecv += i; } - struct message received = {0}; - memcpy(&received, recv_buf + sizeof(buf_len), MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN); - received.text = recv_buf + sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN; - received.len = buf_len - sizeof(buf_len) - MESSAGE_AUTHOR_LEN - MESSAGE_TIMESTAMP_LEN; + memcpy(msg, recv_buf + sizeof(buf_len), MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN); + msg->text = recv_buf + sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN; + msg->text_len = buf_len - sizeof(buf_len) - MESSAGE_AUTHOR_LEN - MESSAGE_TIMESTAMP_LEN; - // assume clientfd is serverfd + 1; - writef("Received %d bytes from client(%d): %s [%s] (%d)%s\n", nrecv, clientfd - 3, received.timestamp, received.author, received.len, received.text); return nrecv; } diff --git a/compile_flags.txt b/v1/compile_flags.txt index 1a62790..1a62790 100644 --- a/compile_flags.txt +++ b/v1/compile_flags.txt @@ -2,7 +2,7 @@ #include "common.h" #include <arpa/inet.h> -#include <errno.h> +#include <assert.h> #include <poll.h> #include <sys/socket.h> #include <unistd.h> @@ -20,12 +20,14 @@ int main(void) serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); - if (bind(serverfd, (struct sockaddr *)&address, sizeof(address))) - return 1; + u32 err = bind(serverfd, (struct sockaddr *)&address, sizeof(address)); + assert(err == 0); - listen(serverfd, 256); + err = listen(serverfd, 256); + assert(err == 0); clientfd = accept(serverfd, 0, 0); + assert(clientfd != -1); struct pollfd fds[1] = { {clientfd, POLLIN, 0}, @@ -33,21 +35,21 @@ int main(void) for (;;) { int ret = poll(fds, 1, 50000); - if (ret == -1) - return 2; + assert(ret != -1); if (fds[0].revents & POLLIN) { u8 recv_buf[BUF_MAX]; u32 nrecv = recv(clientfd, recv_buf, sizeof(recv_buf), 0); + assert(nrecv >= 0); writef("client(%d): %d bytes received.\n", clientfd, nrecv); - if (nrecv == -1) { - return errno; - } else if (nrecv == 0) { + if (nrecv == 0) { writef("client(%d): disconnected.\n", clientfd); - fds[0].fd = -1; + fds[0].fd = -1; fds[0].revents = 0; - close(clientfd); + err = close(clientfd); + assert(err == 0); + return 0; } } @@ -3,7 +3,6 @@ #include <arpa/inet.h> #include <errno.h> #include <signal.h> -#include <string.h> #include <time.h> #include <unistd.h> @@ -17,17 +16,29 @@ void debug_panic(const char *msg) } // get current time in timestamp string -void timestamp(char timestamp[MESSAGE_TIMESTAMP_LEN]) +void timestamp(u8 timestamp[MESSAGE_TIMESTAMP_LEN]) { time_t now; struct tm *ltime; time(&now); ltime = localtime(&now); - strftime(timestamp, MESSAGE_TIMESTAMP_LEN, "%H:%M:%S", ltime); + strftime((char*)timestamp, MESSAGE_TIMESTAMP_LEN, "%H:%M:%S", ltime); } -int main(void) +int main(int argc, char **argv) { + + if (argc < 2) { + printf("usage: send <msg>\n"); + return 1; + } + + Message input = { + .author = "Friendship", + }; + input.text = argv[1]; + input.text_len = str_len(input.text); + serverfd = socket(AF_INET, SOCK_STREAM, 0); if (serverfd == -1) debug_panic("Error while getting socket."); @@ -41,15 +52,10 @@ int main(void) if (connect(serverfd, (struct sockaddr *)&address, sizeof(address))) debug_panic("Error while connecting."); - struct message input = { - .author = "Friendship", - }; - input.text = "Hello from send"; - input.len = str_len(input.text); - printf("input.len: %d\n", input.len); + printf("input.len: %d\n", input.text_len); timestamp(input.timestamp); - send_message(input, serverfd); + message_send(&input, serverfd); return 0; } @@ -12,7 +12,7 @@ #define MAX_CONNECTIONS 5 #define FD_MAX MAX_CONNECTIONS + 1 -static const char *filename = "history.dat"; +// static const u8 *filename = "history.dat"; enum { FD_SERVER = 0 }; u32 serverfd; @@ -31,7 +31,7 @@ int main(void) u32 clientfd; u16 nclient = 0; u32 on = 1; - struct message msg_recv = {0}; + Message msg_recv = {0}; serverfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) @@ -107,7 +107,7 @@ int main(void) clientfd = fds[i].fd; - nrecv = receive_message(&msg_recv, clientfd); + nrecv = message_receive(&msg_recv, clientfd); if (nrecv == 0) { printf("client %d disconnected.\n", i); fds[i].fd = -1; @@ -123,12 +123,14 @@ int main(void) err_exit("Error while receiving from client socket."); } + writef("Received %d bytes from client(%d): %s [%s] (%d)%s\n", nrecv, clientfd - serverfd, msg_recv.timestamp, msg_recv.author, msg_recv.text_len, msg_recv.text); + // TODO: for (u32 j = 1; j <= nclient; j++) { // skip the client that sent the message if (j == i) continue; - if (send(fds[j].fd, &msg_recv, nrecv, 0) == -1) + if (message_send(&msg_recv, fds[j].fd) == -1) printf("Error while sendig message to client %d. errno: %d\n", j, errno); else printf("Retransmitted message to client %d.\n", j); @@ -139,7 +141,6 @@ int main(void) // save_message(&msg_recv, f); // fclose(f); // // return 0; - } } diff --git a/termbox2.h b/v1/termbox2.h index 265cdab..265cdab 100644 --- a/termbox2.h +++ b/v1/termbox2.h |