attempt to start multithreading by creating the socket thread
This commit is contained in:
parent
a87b6c1152
commit
bb71248414
@ -13,6 +13,7 @@ enum error_t {
|
|||||||
ERR_JSON_DESERIALISE,
|
ERR_JSON_DESERIALISE,
|
||||||
ERR_INVALID_REQUEST,
|
ERR_INVALID_REQUEST,
|
||||||
ERR_REQUEST_FAILED,
|
ERR_REQUEST_FAILED,
|
||||||
|
ERR_THREAD,
|
||||||
__ERR_COUNT,
|
__ERR_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,7 +13,8 @@ char const *const ERROR_STRS[] = {
|
|||||||
"JSON SERIALISATION",
|
"JSON SERIALISATION",
|
||||||
"JSON DESERIALISATION",
|
"JSON DESERIALISATION",
|
||||||
"INVALID REQUEST",
|
"INVALID REQUEST",
|
||||||
"REQUEST FAILED"
|
"REQUEST FAILED",
|
||||||
|
"THREAD INITIALISATION"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,3 +30,4 @@ A list of things I'd like to accomplish
|
|||||||
* Remove rendering from serverside
|
* Remove rendering from serverside
|
||||||
* Client-side resource files for each mod
|
* Client-side resource files for each mod
|
||||||
* Real error handling (right now I just pass up to main and immediately exit)
|
* Real error handling (right now I just pass up to main and immediately exit)
|
||||||
|
* Portability to other UNIX-like operating systems (if changes are needed; I don't care about DOS-likes)
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
#ifndef DAEMON_DATA_H
|
#ifndef GAME_DATA_H
|
||||||
#define DAEMON_DATA_H
|
#define GAME_DATA_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <world.h>
|
#include <world.h>
|
||||||
|
|
||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
|
|
||||||
struct data_t {
|
struct game_data_t {
|
||||||
struct options_t options;
|
struct options_t options;
|
||||||
|
|
||||||
struct world_t world;
|
struct world_t world;
|
||||||
|
pthread_mutex_t world_lock;
|
||||||
|
|
||||||
int socket;
|
int socket;
|
||||||
int socket_accept;
|
int socket_accept;
|
@ -12,46 +12,22 @@
|
|||||||
#include <request.h>
|
#include <request.h>
|
||||||
|
|
||||||
#include "opts.h"
|
#include "opts.h"
|
||||||
#include "sock.h"
|
#include "socket.h"
|
||||||
#include "data.h"
|
#include "game_data.h"
|
||||||
#include "request.h"
|
|
||||||
|
|
||||||
static void handle_signal(int signal_no) {
|
static void handle_signal(int signal_no) {
|
||||||
(void)signal_no;
|
(void)signal_no;
|
||||||
|
|
||||||
remove(SOCK_PATH);
|
remove(SOCKET_PATH);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static enum error_t game_loop(struct data_t *data) {
|
|
||||||
enum error_t err = ERR_OK;
|
|
||||||
|
|
||||||
char output_buffer[8192] = { 0 };
|
|
||||||
char input_buffer[8192] = { 0 };
|
|
||||||
|
|
||||||
// Receive request from client
|
|
||||||
read(data->socket_accept, input_buffer, 8192);
|
|
||||||
|
|
||||||
struct request_t request = { 0 };
|
|
||||||
err = request_deserialise_str(&request, input_buffer);
|
|
||||||
if (err != ERR_OK) return err;
|
|
||||||
|
|
||||||
// Send response to client
|
|
||||||
struct response_t response = handle_request(&request, data);
|
|
||||||
err = response_serialise_buf(&response, output_buffer, 8192);
|
|
||||||
if (err != ERR_OK) return err;
|
|
||||||
|
|
||||||
write(data->socket_accept, output_buffer, 8192);
|
|
||||||
|
|
||||||
return ERR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
// Set up variables
|
// Set up variables
|
||||||
enum error_t err = ERR_OK;
|
enum error_t err = ERR_OK;
|
||||||
struct data_t data = { 0 };
|
struct game_data_t data = { 0 };
|
||||||
|
|
||||||
|
|
||||||
// Signal handling; TODO: should probably improve this
|
// Signal handling; TODO: should probably improve this
|
||||||
signal(SIGINT, handle_signal);
|
signal(SIGINT, handle_signal);
|
||||||
@ -70,6 +46,8 @@ int main(int argc, char **argv) {
|
|||||||
err = world_init(&data.world, 10, 10);
|
err = world_init(&data.world, 10, 10);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
|
pthread_mutex_init(&data.world_lock, NULL);
|
||||||
|
|
||||||
err = world_register_entity(&data.world, "john", 'j');
|
err = world_register_entity(&data.world, "john", 'j');
|
||||||
if (err) goto handle_error_world;
|
if (err) goto handle_error_world;
|
||||||
|
|
||||||
@ -82,16 +60,23 @@ int main(int argc, char **argv) {
|
|||||||
entity_init(&data.world.entities[26], 2, 2, 6);
|
entity_init(&data.world.entities[26], 2, 2, 6);
|
||||||
// Make a gap in the entity array to see if "ghost" entities remain
|
// Make a gap in the entity array to see if "ghost" entities remain
|
||||||
|
|
||||||
// Socket handling and run gameloop
|
// Socket handling
|
||||||
err = sock_loop(&data, game_loop);
|
pthread_t pthread_socket;
|
||||||
|
err = socket_thread(&pthread_socket, &data);
|
||||||
if (err) goto handle_error;
|
if (err) goto handle_error;
|
||||||
|
|
||||||
|
pthread_join(pthread_socket, NULL);
|
||||||
|
|
||||||
// Deinitialisation
|
// Deinitialisation
|
||||||
|
pthread_mutex_destroy(&data.world_lock);
|
||||||
|
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
opts_free(&data.options);
|
opts_free(&data.options);
|
||||||
|
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
|
|
||||||
handle_error_world:
|
handle_error_world:
|
||||||
|
pthread_mutex_destroy(&data.world_lock);
|
||||||
world_free(&data.world);
|
world_free(&data.world);
|
||||||
|
|
||||||
handle_error:
|
handle_error:
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
static void handle_request_get_world_data(
|
static void handle_request_get_world_data(
|
||||||
struct response_t *response,
|
struct response_t *response,
|
||||||
struct request_t const *request,
|
struct request_t const *request,
|
||||||
struct data_t const *ctx
|
struct game_data_t const *ctx
|
||||||
) {
|
) {
|
||||||
assert(response != NULL);
|
assert(response != NULL);
|
||||||
assert(request != NULL);
|
assert(request != NULL);
|
||||||
@ -26,7 +26,7 @@ static void handle_request_get_world_data(
|
|||||||
|
|
||||||
struct response_t handle_request(
|
struct response_t handle_request(
|
||||||
struct request_t const *request,
|
struct request_t const *request,
|
||||||
struct data_t *ctx
|
struct game_data_t *ctx
|
||||||
) {
|
) {
|
||||||
assert(request != NULL);
|
assert(request != NULL);
|
||||||
assert(ctx != NULL);
|
assert(ctx != NULL);
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
|
|
||||||
#include <request.h>
|
#include <request.h>
|
||||||
|
|
||||||
#include "data.h"
|
#include "game_data.h"
|
||||||
|
|
||||||
struct response_t handle_request(
|
struct response_t handle_request(
|
||||||
struct request_t const *,
|
struct request_t const *,
|
||||||
struct data_t *
|
struct game_data_t *
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
// sock.c
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/un.h>
|
|
||||||
|
|
||||||
#include "sock.h"
|
|
||||||
|
|
||||||
enum error_t sock_init(int *sockptr) {
|
|
||||||
assert(sockptr != NULL);
|
|
||||||
|
|
||||||
int const sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
||||||
if (sock < 0) goto sock_init_error;
|
|
||||||
|
|
||||||
struct sockaddr_un sa = { 0 };
|
|
||||||
sa.sun_family = AF_UNIX;
|
|
||||||
|
|
||||||
strcpy(sa.sun_path, SOCK_PATH);
|
|
||||||
remove(SOCK_PATH);
|
|
||||||
// Should be redundant but it doesn't hurt to be sure
|
|
||||||
|
|
||||||
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) goto sock_error;
|
|
||||||
if (listen(sock, 4096) < 0) goto sock_error;
|
|
||||||
|
|
||||||
*sockptr = sock;
|
|
||||||
return ERR_OK;
|
|
||||||
|
|
||||||
sock_error:
|
|
||||||
sock_free(&sock);
|
|
||||||
|
|
||||||
sock_init_error:
|
|
||||||
return ERR_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sock_free(int const *sockptr) {
|
|
||||||
assert(sockptr != NULL);
|
|
||||||
|
|
||||||
close(*sockptr);
|
|
||||||
remove(SOCK_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum error_t sock_loop(struct data_t *data, gameloop_fn fn) {
|
|
||||||
assert(data != NULL);
|
|
||||||
|
|
||||||
enum error_t err = ERR_OK;
|
|
||||||
|
|
||||||
if ((err = sock_init(&data->socket))) return err;
|
|
||||||
|
|
||||||
for (int sock_accept;;) {
|
|
||||||
if ((sock_accept = accept(data->socket, NULL, NULL)) < 0) {
|
|
||||||
err = ERR_SOCKET;
|
|
||||||
goto sock_loop_end_pre;
|
|
||||||
};
|
|
||||||
|
|
||||||
data->socket_accept = sock_accept;
|
|
||||||
if ((err = fn(data))) goto sock_loop_end_pre;
|
|
||||||
|
|
||||||
close(data->socket_accept);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sock_loop_end_pre:
|
|
||||||
close(data->socket_accept);
|
|
||||||
goto sock_loop_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
sock_loop_end:
|
|
||||||
sock_free(&data->socket);
|
|
||||||
return err;
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#ifndef SOCK_H
|
|
||||||
#define SOCK_H
|
|
||||||
|
|
||||||
#include <error.h>
|
|
||||||
|
|
||||||
#include "data.h"
|
|
||||||
|
|
||||||
#define SOCK_PATH "/tmp/swd.sock"
|
|
||||||
// Socket path should be a shared setting between server and client
|
|
||||||
|
|
||||||
enum error_t sock_init(int *);
|
|
||||||
void sock_free(int const *);
|
|
||||||
|
|
||||||
typedef enum error_t (* gameloop_fn)(struct data_t *);
|
|
||||||
enum error_t sock_loop(struct data_t *, gameloop_fn);
|
|
||||||
|
|
||||||
#endif
|
|
107
server/src/socket.c
Normal file
107
server/src/socket.c
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// sock.c
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include "socket.h"
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
enum error_t socket_init(int *sockptr) {
|
||||||
|
assert(sockptr != NULL);
|
||||||
|
|
||||||
|
int const sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) goto error;
|
||||||
|
|
||||||
|
struct sockaddr_un sa = { 0 };
|
||||||
|
sa.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
strcpy(sa.sun_path, SOCKET_PATH);
|
||||||
|
remove(SOCKET_PATH); // Should be redundant but it doesn't hurt to be sure
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa))) goto error_free_sock;
|
||||||
|
if (listen(sock, 4096) < 0) goto error_free_sock;
|
||||||
|
|
||||||
|
*sockptr = sock;
|
||||||
|
return ERR_OK;
|
||||||
|
|
||||||
|
error_free_sock:
|
||||||
|
socket_free(&sock);
|
||||||
|
error:
|
||||||
|
return ERR_SOCKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void socket_free(int const *sockptr) {
|
||||||
|
assert(sockptr != NULL);
|
||||||
|
|
||||||
|
close(*sockptr);
|
||||||
|
remove(SOCKET_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static enum error_t socket_handle(struct game_data_t *data) {
|
||||||
|
enum error_t err = ERR_OK;
|
||||||
|
|
||||||
|
char output_buffer[8192] = { 0 };
|
||||||
|
char input_buffer[8192] = { 0 };
|
||||||
|
|
||||||
|
// Receive request from client
|
||||||
|
read(data->socket_accept, input_buffer, 8192);
|
||||||
|
|
||||||
|
struct request_t request = { 0 };
|
||||||
|
err = request_deserialise_str(&request, input_buffer);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
// Send response to client
|
||||||
|
struct response_t response = handle_request(&request, data);
|
||||||
|
err = response_serialise_buf(&response, output_buffer, 8192);
|
||||||
|
if (err != ERR_OK) return err;
|
||||||
|
|
||||||
|
write(data->socket_accept, output_buffer, 8192);
|
||||||
|
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *socket_thread_fn(void *data_void) {
|
||||||
|
assert(data_void != NULL);
|
||||||
|
|
||||||
|
struct game_data_t *data = (struct game_data_t *)data_void;
|
||||||
|
|
||||||
|
enum error_t err = ERR_OK;
|
||||||
|
|
||||||
|
if ((err = socket_init(&data->socket))) return (void *)err;
|
||||||
|
|
||||||
|
for (int sock_accept;;) {
|
||||||
|
if ((sock_accept = accept(data->socket, NULL, NULL)) < 0) {
|
||||||
|
err = ERR_SOCKET;
|
||||||
|
goto error_pre;
|
||||||
|
};
|
||||||
|
|
||||||
|
data->socket_accept = sock_accept;
|
||||||
|
if ((err = socket_handle(data))) goto error_pre;
|
||||||
|
|
||||||
|
close(data->socket_accept);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
error_pre:
|
||||||
|
close(data->socket_accept);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
socket_free(&data->socket);
|
||||||
|
return (void *)err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum error_t socket_thread(pthread_t *pthread, struct game_data_t *data) {
|
||||||
|
int err = pthread_create(pthread, NULL, socket_thread_fn, data);
|
||||||
|
if (err != 0) return ERR_THREAD;
|
||||||
|
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
16
server/src/socket.h
Normal file
16
server/src/socket.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef SOCKET_H
|
||||||
|
#define SOCKET_H
|
||||||
|
|
||||||
|
#include <error.h>
|
||||||
|
|
||||||
|
#include "game_data.h"
|
||||||
|
|
||||||
|
#define SOCKET_PATH "/tmp/swd.sock"
|
||||||
|
// Socket path should be a shared setting between server and client
|
||||||
|
|
||||||
|
enum error_t socket_init(int *);
|
||||||
|
void socket_free(int const *);
|
||||||
|
|
||||||
|
enum error_t socket_thread(pthread_t *, struct game_data_t *);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user