initial
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
Makefile
|
||||
svrtest
|
||||
main
|
||||
CMakeFiles/
|
||||
.vscode/
|
||||
build/
|
||||
70
CMakeLists.txt
Normal file
70
CMakeLists.txt
Normal file
@@ -0,0 +1,70 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(miniboinc
|
||||
VERSION 0.1.0
|
||||
LANGUAGES C
|
||||
)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Output directories
|
||||
# ---------------------------------------------------------
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
|
||||
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
||||
endforeach()
|
||||
|
||||
# Common
|
||||
file(GLOB_RECURSE COMMON_SRC CONFIGURE_DEPENDS src/common/*.c)
|
||||
add_library(common STATIC ${COMMON_SRC})
|
||||
target_link_libraries(common PUBLIC)
|
||||
target_compile_options(common PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-lpthread
|
||||
)
|
||||
target_include_directories(common PUBLIC
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
# Client
|
||||
file(GLOB_RECURSE CLIENT_SRC CONFIGURE_DEPENDS src/client/*.c)
|
||||
add_executable(client ${CLIENT_SRC})
|
||||
target_link_libraries(client PRIVATE common)
|
||||
target_include_directories(client PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
target_compile_options(client PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-lpthread
|
||||
)
|
||||
set_target_properties(client PROPERTIES OUTPUT_NAME "miniboinc_client")
|
||||
|
||||
# Server
|
||||
file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/server/*.c)
|
||||
add_executable(server ${SERVER_SRC})
|
||||
target_link_libraries(server PRIVATE common)
|
||||
target_include_directories(server PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
target_compile_options(server PRIVATE
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-lpthread
|
||||
-g
|
||||
)
|
||||
target_compile_definitions(server PRIVATE)
|
||||
set_target_properties(server PROPERTIES OUTPUT_NAME "miniboinc_server")
|
||||
57
include/common/dynarr.h
Normal file
57
include/common/dynarr.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef DYNARR_H
|
||||
#define DYNARR_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DYNARR_MAX_CAPACITY ((size_t)0x7FFFFFFF)
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
size_t elemSize;
|
||||
size_t capacity;
|
||||
void* data;
|
||||
} DynArr;
|
||||
|
||||
// Do not use; Use DYNARR_CREATE macro instead.
|
||||
DynArr* DynArr_create(size_t elemSize, size_t capacity);
|
||||
|
||||
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
|
||||
void DynArr_reserve(DynArr* p, size_t n);
|
||||
|
||||
// Push data into a new block at the end of the array
|
||||
void* DynArr_push_back(DynArr* p, void* value);
|
||||
|
||||
// Remove the last block in the array.
|
||||
void DynArr_pop_back(DynArr* p);
|
||||
|
||||
// Remove first block from array.
|
||||
void DynArr_pop_front(DynArr* p);
|
||||
|
||||
// Remove index from array. This moves all blocks after the index block.
|
||||
void DynArr_remove(DynArr* p, size_t index);
|
||||
|
||||
// Erase the array. This will not free unused blocks.
|
||||
void DynArr_erase(DynArr* p);
|
||||
|
||||
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
|
||||
void DynArr_prune(DynArr* p);
|
||||
|
||||
// Get a pointer to a block by index
|
||||
void* DynArr_at(DynArr* p, size_t index);
|
||||
|
||||
// Get the index by block pointer
|
||||
size_t DynArr_at_ptr(DynArr* p, void* ptr);
|
||||
|
||||
// Get size
|
||||
size_t DynArr_size(DynArr* p);
|
||||
|
||||
// Get capacity
|
||||
size_t DynArr_capacity(DynArr* p);
|
||||
|
||||
void DynArr_destroy(DynArr* p);
|
||||
|
||||
#define DYNARR_CREATE(T, initialCapacity) DynArr_create(sizeof(T), initialCapacity)
|
||||
|
||||
#endif
|
||||
12
include/common/numgen.h
Normal file
12
include/common/numgen.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef NUMGEN_H
|
||||
#define NUMGEN_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
unsigned char random_byte(void);
|
||||
uint32_t random_four_byte(void);
|
||||
|
||||
#endif
|
||||
46
include/common/task/task.h
Normal file
46
include/common/task/task.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef TASK_H
|
||||
#define TASK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <common/dynarr.h>
|
||||
|
||||
typedef enum {
|
||||
TASK_NONE,
|
||||
TASK_PENDING,
|
||||
TASK_ASSIGNED,
|
||||
TASK_DONE,
|
||||
TASK_FAILED
|
||||
} task_state_t;
|
||||
|
||||
extern int TASK_ERR_CODE;
|
||||
|
||||
typedef enum {
|
||||
TASK_ERR_NONE,
|
||||
TASK_ERR_INVPTR,
|
||||
TASK_ERR_INVARGS,
|
||||
TASK_ERR_INVQUEUE,
|
||||
TASK_ERR_INVIDX,
|
||||
TASK_ERR_INVTASK
|
||||
} task_err;
|
||||
|
||||
typedef struct {
|
||||
char name[32]; // string
|
||||
DynArr* data;
|
||||
} task_arg_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t taskId;
|
||||
char binary[64];
|
||||
DynArr* args;
|
||||
task_state_t state;
|
||||
uint32_t assigned_to;
|
||||
time_t assigned_at;
|
||||
} task_t;
|
||||
|
||||
void Task_Create(task_t* tsk);
|
||||
void Task_DestroyArgs(task_t* task);
|
||||
|
||||
#endif
|
||||
27
include/common/tcpd/tcpclient.h
Normal file
27
include/common/tcpd/tcpclient.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef TCPCLIENT_H
|
||||
#define TCPCLIENT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define MTU 1500
|
||||
|
||||
struct TcpClient {
|
||||
int clientFd;
|
||||
struct sockaddr_in clientAddr;
|
||||
uint32_t clientId;
|
||||
|
||||
unsigned char dataBuf[MTU];
|
||||
void (*on_data)(struct TcpClient* client);
|
||||
void (*on_disconnect)(struct TcpClient* client);
|
||||
|
||||
pthread_t clientThread;
|
||||
};
|
||||
|
||||
typedef struct TcpClient TcpClient;
|
||||
|
||||
#endif
|
||||
54
include/common/tcpd/tcpserver.h
Normal file
54
include/common/tcpd/tcpserver.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef TCPSERVER_H
|
||||
#define TCPSERVER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common/tcpd/tcpclient.h>
|
||||
#include <common/numgen.h>
|
||||
|
||||
typedef struct {
|
||||
int sockFd;
|
||||
struct sockaddr_in addr;
|
||||
int opt;
|
||||
|
||||
// Called before the client thread runs
|
||||
void (*on_connect)(TcpClient* client);
|
||||
// Called when data is received
|
||||
void (*on_data)(TcpClient* client);
|
||||
// Called before the socket and client thread are killed; Do NOT free client manually
|
||||
void (*on_disconnect)(TcpClient* client);
|
||||
|
||||
// max clients
|
||||
size_t clients;
|
||||
TcpClient** clientsArrPtr;
|
||||
|
||||
pthread_t svrThread;
|
||||
} TcpServer;
|
||||
|
||||
struct tcpclient_thread_args {
|
||||
TcpClient* clientPtr;
|
||||
TcpServer* serverPtr;
|
||||
};
|
||||
|
||||
typedef struct tcpclient_thread_args tcpclient_thread_args;
|
||||
|
||||
TcpServer* TcpServer_Create();
|
||||
void TcpServer_Destroy(TcpServer* ptr);
|
||||
|
||||
void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr);
|
||||
void TcpServer_Start(TcpServer* ptr, int maxcons);
|
||||
void TcpServer_Stop(TcpServer* ptr);
|
||||
void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len);
|
||||
void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli);
|
||||
void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli);
|
||||
|
||||
size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len);
|
||||
|
||||
#endif
|
||||
17
include/server/task/taskqueue.h
Normal file
17
include/server/task/taskqueue.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef TASKQUEUE_H
|
||||
#define TASKQUEUE_H
|
||||
|
||||
#include <common/task/task.h>
|
||||
#include <common/dynarr.h>
|
||||
|
||||
typedef struct {
|
||||
DynArr* tasks;
|
||||
} task_queue_t;
|
||||
|
||||
void TaskQueue_Init(task_queue_t* queue);
|
||||
void TaskQueue_DestroyQueue(task_queue_t* queue);
|
||||
void TaskQueue_AddTask(task_queue_t* queue, task_t* task);
|
||||
void TaskQueue_RemoveTask(task_queue_t* queue, size_t idx);
|
||||
void TaskQueue_RemoveTaskByPtr(task_queue_t* queue, task_t* task);
|
||||
|
||||
#endif
|
||||
6
src/client/main.c
Normal file
6
src/client/main.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
||||
164
src/common/dynarr.c
Normal file
164
src/common/dynarr.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include <common/dynarr.h>
|
||||
|
||||
DynArr* DynArr_create(size_t elemSize, size_t capacity) {
|
||||
DynArr* p = (DynArr*)malloc(sizeof(DynArr));
|
||||
if (!p) return NULL;
|
||||
|
||||
p->elemSize = elemSize;
|
||||
p->capacity = capacity;
|
||||
p->data = malloc(elemSize * capacity);
|
||||
if (!p->data) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
p->size = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
|
||||
void DynArr_reserve(DynArr* p, size_t n) {
|
||||
if (n <= p->capacity) {
|
||||
printf("reserve ignored; attempted to reserve less or equal to current capacity\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (n > DYNARR_MAX_CAPACITY) {
|
||||
printf("reserve ignored; attempted to reserve over 32 bits\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void* new_data = realloc(p->data, n * p->elemSize);
|
||||
if (!new_data) {
|
||||
printf("reserve failed\n");
|
||||
exit(1);
|
||||
}
|
||||
p->data = new_data;
|
||||
p->capacity = n;
|
||||
}
|
||||
|
||||
// Push data into a new block at the end of the array
|
||||
void* DynArr_push_back(DynArr* p, void* value) {
|
||||
if (value == NULL) {
|
||||
printf("push_back ignored; value is null");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (p->size >= p->capacity) {
|
||||
size_t new_cap = (p->capacity == 0) ? 1 : p->capacity * 2;
|
||||
|
||||
if (new_cap < p->capacity || new_cap > DYNARR_MAX_CAPACITY) {
|
||||
printf("push_back ignored; capacity overflow\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* new_data = realloc(p->data, new_cap * p->elemSize);
|
||||
if (!new_data) {
|
||||
printf("push failed\n");
|
||||
exit(1);
|
||||
}
|
||||
p->capacity = new_cap;
|
||||
p->data = new_data;
|
||||
}
|
||||
|
||||
void* dst = (void*)((char*)p->data + (p->size * p->elemSize));
|
||||
memcpy((char*)dst, value, p->elemSize);
|
||||
p->size++;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
// Remove the last block in the array.
|
||||
void DynArr_pop_back(DynArr* p) {
|
||||
if (p->size == 0) {
|
||||
printf("pop_back ignored; size is 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
p->size--; // Will automatically overwrite that memory naturally
|
||||
}
|
||||
|
||||
// Remove first block from array.
|
||||
void DynArr_pop_front(DynArr* p) {
|
||||
if (p->size == 0) {
|
||||
printf("pop_front ignored; size is 0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(
|
||||
(char*)p->data,
|
||||
(char*)p->data + p->elemSize,
|
||||
(p->size - 1) * p->elemSize
|
||||
);
|
||||
|
||||
p->size--;
|
||||
}
|
||||
|
||||
// Remove index from array. This moves all blocks after the index block.
|
||||
void DynArr_remove(DynArr* p, size_t index) {
|
||||
if (index >= p->size) return;
|
||||
|
||||
memmove(
|
||||
(char*)p->data + (index * p->elemSize),
|
||||
(char*)p->data + (index + 1) * p->elemSize,
|
||||
(p->size - index - 1) * p->elemSize
|
||||
);
|
||||
|
||||
p->size--;
|
||||
}
|
||||
|
||||
// Erase the array. This will not free unused blocks.
|
||||
void DynArr_erase(DynArr* p) {
|
||||
p->size = 0;
|
||||
}
|
||||
|
||||
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
|
||||
void DynArr_prune(DynArr* p) {
|
||||
void* new_data = realloc(p->data, (p->size == 0 ? 1 : p->size) * p->elemSize);
|
||||
if (!new_data) {
|
||||
printf("pruning failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
p->data = new_data;
|
||||
p->capacity = p->size;
|
||||
}
|
||||
|
||||
// Get a pointer to a block by index
|
||||
void* DynArr_at(DynArr* p, size_t index) {
|
||||
if (index >= p->size) return NULL;
|
||||
return (char*)p->data + (index * p->elemSize);
|
||||
}
|
||||
|
||||
// Get the index by block pointer
|
||||
size_t DynArr_at_ptr(DynArr* p, void* ptr) {
|
||||
if (!p || !ptr) {
|
||||
printf("invalid pointer\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < p->size; i++) {
|
||||
if ((void*)(((char*)p->data) + (i * p->elemSize)) == ptr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// If for some reason the array has 2^64 elements in it, then fuck it, I guess we'll just crash, I don't care.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get size
|
||||
size_t DynArr_size(DynArr* p) {
|
||||
return p->size;
|
||||
}
|
||||
|
||||
// Get capacity
|
||||
size_t DynArr_capacity(DynArr* p) {
|
||||
return p->capacity;
|
||||
}
|
||||
|
||||
void DynArr_destroy(DynArr* p) {
|
||||
if (!p) return;
|
||||
free(p->data);
|
||||
free(p);
|
||||
}
|
||||
17
src/common/numgen.c
Normal file
17
src/common/numgen.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <common/numgen.h>
|
||||
|
||||
unsigned char random_byte(void) {
|
||||
return (unsigned char)(rand() % 256);
|
||||
}
|
||||
|
||||
uint32_t random_four_byte(void) {
|
||||
uint32_t x;
|
||||
unsigned char bytes[4];
|
||||
for (char i = 0; i < 4; i++) {
|
||||
bytes[i] = random_byte();
|
||||
}
|
||||
|
||||
memcpy(&x, bytes, sizeof(x));
|
||||
|
||||
return x;
|
||||
}
|
||||
33
src/common/task/task.c
Normal file
33
src/common/task/task.c
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <common/task/task.h>
|
||||
#include <common/numgen.h>
|
||||
|
||||
// Last encountered error after ANY event.
|
||||
int TASK_ERR_CODE = TASK_ERR_NONE;
|
||||
|
||||
void Task_Create(task_t* tsk) {
|
||||
if (!tsk) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
tsk->taskId = random_four_byte();
|
||||
tsk->assigned_at = 0; // or NULL, so 1/1/1970
|
||||
tsk->assigned_to = 0; // ClientID 0 is reserved
|
||||
tsk->state = TASK_NONE;
|
||||
memset(tsk->binary, 0, 64);
|
||||
tsk->args = DYNARR_CREATE(task_arg_t, 1);
|
||||
}
|
||||
|
||||
void Task_DestroyArgs(task_t* task) {
|
||||
if (!task) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!task->args) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVARGS;
|
||||
return;
|
||||
}
|
||||
|
||||
DynArr_destroy(task->args);
|
||||
}
|
||||
292
src/common/tcpd/tcpserver.c
Normal file
292
src/common/tcpd/tcpserver.c
Normal file
@@ -0,0 +1,292 @@
|
||||
#include <common/tcpd/tcpserver.h>
|
||||
|
||||
TcpServer* TcpServer_Create() {
|
||||
TcpServer* svr = (TcpServer*)malloc(sizeof(TcpServer));
|
||||
|
||||
if (!svr) {
|
||||
perror("tcpserver - creation failure");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
svr->sockFd = -1;
|
||||
svr->svrThread = 0;
|
||||
svr->on_connect = NULL;
|
||||
svr->on_data = NULL;
|
||||
svr->on_disconnect = NULL;
|
||||
|
||||
svr->clients = 0;
|
||||
svr->clientsArrPtr = NULL;
|
||||
|
||||
return svr;
|
||||
}
|
||||
|
||||
void TcpServer_Destroy(TcpServer* ptr) {
|
||||
if (ptr) {
|
||||
if (ptr->clientsArrPtr) {
|
||||
for (size_t i = 0; i < ptr->clients; i++) {
|
||||
if (ptr->clientsArrPtr[i]) {
|
||||
free(ptr->clientsArrPtr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(ptr->clientsArrPtr);
|
||||
}
|
||||
|
||||
close(ptr->sockFd);
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void TcpServer_Init(TcpServer* ptr, unsigned short port, const char* addr) {
|
||||
if (ptr) {
|
||||
// Create socket
|
||||
ptr->sockFd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (ptr->sockFd < 0) {
|
||||
perror("tcpserver - socket");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Allow quick port resue
|
||||
ptr->opt = 1;
|
||||
setsockopt(ptr->sockFd, SOL_SOCKET, SO_REUSEADDR, &ptr->opt, sizeof(int));
|
||||
|
||||
// Fill address structure
|
||||
memset(&ptr->addr, 0, sizeof(ptr->addr));
|
||||
ptr->addr.sin_family = AF_INET;
|
||||
ptr->addr.sin_port = htons(port);
|
||||
inet_pton(AF_INET, addr, &ptr->addr.sin_addr);
|
||||
|
||||
// Bind
|
||||
if (bind(ptr->sockFd, (struct sockaddr*)&ptr->addr, sizeof(ptr->addr)) < 0) {
|
||||
perror("tcpserver - bind");
|
||||
close(ptr->sockFd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not call outside of func.
|
||||
void* TcpServer_clientthreadprocess(void* ptr) {
|
||||
if (!ptr) {
|
||||
perror("Client ptr is null!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tcpclient_thread_args* args = (tcpclient_thread_args*)ptr;
|
||||
|
||||
TcpClient* cli = args->clientPtr;
|
||||
TcpServer* svr = args->serverPtr;
|
||||
|
||||
if (args) {
|
||||
free(args);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
memset(cli->dataBuf, 0, MTU); // Reset buffer
|
||||
ssize_t n = recv(cli->clientFd, cli->dataBuf, MTU, 0);
|
||||
|
||||
if (n == 0) {
|
||||
break; // Client disconnected
|
||||
} else if (n > 0) {
|
||||
if (cli->on_data) {
|
||||
cli->on_data(cli);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_testcancel(); // Check for thread death
|
||||
}
|
||||
|
||||
cli->on_disconnect(cli);
|
||||
|
||||
// Close on exit
|
||||
close(cli->clientFd);
|
||||
|
||||
// Destroy
|
||||
TcpClient** arr = svr->clientsArrPtr;
|
||||
size_t idx = Generic_FindClientInArrayByPtr(arr, cli, svr->clients);
|
||||
if (idx != SIZE_MAX) {
|
||||
if (arr[idx]) {
|
||||
free(arr[idx]);
|
||||
arr[idx] = NULL;
|
||||
}
|
||||
} else {
|
||||
perror("tcpserver (client thread) - something already freed the client!");
|
||||
}
|
||||
|
||||
//free(ptr);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Do not call outside of func.
|
||||
void* TcpServer_threadprocess(void* ptr) {
|
||||
if (!ptr) {
|
||||
perror("Client ptr is null!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TcpServer* svr = (TcpServer*)ptr;
|
||||
while (1) {
|
||||
TcpClient tempclient;
|
||||
socklen_t clientsize = sizeof(tempclient.clientAddr);
|
||||
int client = accept(svr->sockFd, (struct sockaddr*)&tempclient.clientAddr, &clientsize);
|
||||
if (client >= 0) {
|
||||
tempclient.clientFd = client;
|
||||
tempclient.on_data = svr->on_data;
|
||||
tempclient.on_disconnect = svr->on_disconnect;
|
||||
|
||||
// I'm lazy, so I'm just copying the data for now (I should probably make this a better way)
|
||||
TcpClient* heapCli = (TcpClient*)malloc(sizeof(TcpClient));
|
||||
if (!heapCli) {
|
||||
perror("tcpserver - client failed to allocate");
|
||||
exit(EXIT_FAILURE); // Wtf just happened???
|
||||
}
|
||||
|
||||
heapCli->clientAddr = tempclient.clientAddr;
|
||||
heapCli->clientFd = tempclient.clientFd;
|
||||
heapCli->on_data = tempclient.on_data;
|
||||
heapCli->on_disconnect = tempclient.on_disconnect;
|
||||
heapCli->clientId = random_four_byte();
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < svr->clients; i++) {
|
||||
if (svr->clientsArrPtr[i] == NULL) {
|
||||
// Make use of that space
|
||||
svr->clientsArrPtr[i] = heapCli; // We have now transfered the ownership :)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == svr->clients) {
|
||||
// Not found
|
||||
// RST; Thread doesn't exist yet
|
||||
struct linger so_linger;
|
||||
so_linger.l_onoff = 1;
|
||||
so_linger.l_linger = 0;
|
||||
setsockopt(heapCli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
|
||||
close(heapCli->clientFd);
|
||||
|
||||
free(heapCli);
|
||||
heapCli = NULL;
|
||||
//svr->clientsArrPtr[i] = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
tcpclient_thread_args* arg = (tcpclient_thread_args*)malloc(sizeof(tcpclient_thread_args));
|
||||
arg->clientPtr = heapCli;
|
||||
arg->serverPtr = svr;
|
||||
|
||||
if (svr->on_connect) {
|
||||
svr->on_connect(heapCli);
|
||||
}
|
||||
|
||||
pthread_create(&heapCli->clientThread, NULL, TcpServer_clientthreadprocess, arg);
|
||||
pthread_detach(heapCli->clientThread); // May not work :(
|
||||
}
|
||||
|
||||
pthread_testcancel(); // Check for thread death
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TcpServer_Start(TcpServer* ptr, int maxcons) {
|
||||
if (ptr) {
|
||||
if (listen(ptr->sockFd, maxcons) < 0) {
|
||||
perror("tcpserver - listen");
|
||||
close(ptr->sockFd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ptr->clients = maxcons;
|
||||
ptr->clientsArrPtr = (TcpClient**)malloc(sizeof(TcpClient*) * maxcons);
|
||||
|
||||
if (!ptr->clientsArrPtr) {
|
||||
perror("tcpserver - allocation of client space fatally errored");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Fucking null out everything
|
||||
for (int i = 0; i < maxcons; i++) {
|
||||
ptr->clientsArrPtr[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Spawn server thread
|
||||
pthread_create(&ptr->svrThread, NULL, TcpServer_threadprocess, ptr);
|
||||
}
|
||||
|
||||
void TcpServer_Stop(TcpServer* ptr) {
|
||||
if (ptr && ptr->svrThread != 0) {
|
||||
// Stop server
|
||||
pthread_cancel(ptr->svrThread);
|
||||
pthread_join(ptr->svrThread, NULL);
|
||||
|
||||
// Disconnect clients
|
||||
for (size_t i = 0; i < ptr->clients; i++) {
|
||||
TcpClient* cliPtr = ptr->clientsArrPtr[i];
|
||||
if (cliPtr) {
|
||||
close(cliPtr->clientFd);
|
||||
pthread_cancel(cliPtr->clientThread);
|
||||
}
|
||||
}
|
||||
|
||||
ptr->svrThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void TcpServer_Send(TcpServer* ptr, TcpClient* cli, void* data, size_t len) {
|
||||
if (ptr && cli && data && len > 0) {
|
||||
send(cli->clientFd, data, len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void TcpServer_Disconnect(TcpServer* ptr, TcpClient* cli) {
|
||||
if (ptr && cli) {
|
||||
close(cli->clientFd);
|
||||
pthread_cancel(cli->clientThread);
|
||||
|
||||
size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients);
|
||||
if (idx != SIZE_MAX) {
|
||||
if (ptr->clientsArrPtr[idx]) {
|
||||
free(ptr->clientsArrPtr[idx]);
|
||||
}
|
||||
ptr->clientsArrPtr[idx] = NULL;
|
||||
} else {
|
||||
perror("tcpserver - didn't find client to disconnect in array!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TcpServer_KillClient(TcpServer* ptr, TcpClient* cli) {
|
||||
if (ptr && cli) {
|
||||
// RST the connection
|
||||
struct linger so_linger;
|
||||
so_linger.l_onoff = 1;
|
||||
so_linger.l_linger = 0;
|
||||
setsockopt(cli->clientFd, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
|
||||
close(cli->clientFd);
|
||||
pthread_cancel(cli->clientThread);
|
||||
|
||||
size_t idx = Generic_FindClientInArrayByPtr(ptr->clientsArrPtr, cli, ptr->clients);
|
||||
if (idx != SIZE_MAX) {
|
||||
if (ptr->clientsArrPtr[idx]) {
|
||||
free(ptr->clientsArrPtr[idx]);
|
||||
}
|
||||
ptr->clientsArrPtr[idx] = NULL;
|
||||
} else {
|
||||
perror("tcpserver - didn't find client to kill in array!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t Generic_FindClientInArrayByPtr(TcpClient** arr, TcpClient* ptr, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (arr[i] == ptr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return SIZE_MAX; // Returns max unsigned, likely improbable to be correct
|
||||
}
|
||||
39
src/server/main.c
Normal file
39
src/server/main.c
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <server/task/taskqueue.h>
|
||||
#include <common/task/task.h>
|
||||
#include <unistd.h>
|
||||
#include <common/tcpd/tcpserver.h>
|
||||
#include <signal.h>
|
||||
|
||||
volatile int running = 1;
|
||||
|
||||
void signalHandler(int sig) {
|
||||
running = 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
signal(SIGINT, signalHandler);
|
||||
|
||||
task_queue_t taskQueue;
|
||||
TaskQueue_Init(&taskQueue);
|
||||
|
||||
printf("Task Queue Created!\n");
|
||||
task_t task1;
|
||||
Task_Create(&task1);
|
||||
task1.state = TASK_PENDING;
|
||||
|
||||
TaskQueue_AddTask(&taskQueue, &task1);
|
||||
printf("Task added!\n");
|
||||
|
||||
while (running) {
|
||||
// Main Loop
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
TaskQueue_RemoveTask(&taskQueue, 0);
|
||||
TaskQueue_DestroyQueue(&taskQueue);
|
||||
|
||||
printf("Destroyed!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
93
src/server/task/taskqueue.c
Normal file
93
src/server/task/taskqueue.c
Normal file
@@ -0,0 +1,93 @@
|
||||
#include <server/task/taskqueue.h>
|
||||
|
||||
void TaskQueue_Init(task_queue_t* queue) {
|
||||
if (!queue) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVQUEUE;
|
||||
return;
|
||||
}
|
||||
|
||||
queue->tasks = DYNARR_CREATE(task_t, 1);
|
||||
}
|
||||
|
||||
void TaskQueue_DestroyQueue(task_queue_t* queue) {
|
||||
if (!queue) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVQUEUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue->tasks) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
DynArr_destroy(queue->tasks);
|
||||
}
|
||||
|
||||
void TaskQueue_AddTask(task_queue_t* queue, task_t* task) {
|
||||
if (!queue) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVQUEUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue->tasks) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!task) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVTASK;
|
||||
return;
|
||||
}
|
||||
|
||||
DynArr_push_back(queue->tasks, task);
|
||||
}
|
||||
|
||||
void TaskQueue_RemoveTask(task_queue_t* queue, size_t idx) {
|
||||
if (!queue) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVQUEUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue->tasks) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t n = DynArr_size(queue->tasks);
|
||||
|
||||
if (idx >= n) { // Index adjusted
|
||||
TASK_ERR_CODE = TASK_ERR_INVIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
task_t* ptr = DynArr_at(queue->tasks, idx);
|
||||
Task_DestroyArgs(ptr);
|
||||
|
||||
DynArr_remove(queue->tasks, n);
|
||||
}
|
||||
|
||||
void TaskQueue_RemoveTaskByPtr(task_queue_t* queue, task_t* task) {
|
||||
if (!queue) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVQUEUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!queue->tasks) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVPTR;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!task) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVTASK;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t at = DynArr_at_ptr(queue->tasks, task);
|
||||
|
||||
if (at == SIZE_MAX) {
|
||||
TASK_ERR_CODE = TASK_ERR_INVIDX;
|
||||
return;
|
||||
}
|
||||
|
||||
DynArr_remove(queue->tasks, at);
|
||||
}
|
||||
Reference in New Issue
Block a user