commit 6f09e6ad09ec01253320350acbfcbe80c1ab370d Author: James Booth Date: Mon May 11 22:39:46 2015 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fac8c0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +Makefile +Makefile.in +aclocal.m4 +autom4te.cache/ +build-aux/ +config.log +config.status +configure +src/config.h +src/config.h.in +src/config.h.in~ +src/server/.deps/ +src/server/.dirstamp +src/server/clients.o +src/server/server.o +src/stamp-h1 + +.codelite/ +stabber.project +stabber.workspace + +server + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..06a6612 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ +bin_PROGRAMS = server +server_SOURCES = src/server/server.c src/server/clients.h src/server/clients.c diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..c5a7472 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf --install diff --git a/configure-debug b/configure-debug new file mode 100755 index 0000000..85bfd81 --- /dev/null +++ b/configure-debug @@ -0,0 +1,3 @@ +#!/bin/sh + +./configure CFLAGS='-g3 -O0' CXXFLAGS='-g3 -O0' diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..b3794a8 --- /dev/null +++ b/configure.ac @@ -0,0 +1,32 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_INIT([stabber], [0.1.0], [boothj5web@gmail.com]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SRCDIR([src/server/server.c]) +AC_CONFIG_HEADERS([src/config.h]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) + +### Checks for programs. +AC_PROG_CC + +PKG_CHECK_MODULES([glib], [glib-2.0 >= 2.26], [], + [AC_MSG_ERROR([glib 2.26 or higher is required])]) + +AC_CHECK_LIB([pthread], [main], [], + [AC_MSG_ERROR([pthread is required])]) + +AM_CFLAGS="-Wall -Wno-deprecated-declarations" +AM_CFLAGS="$AM_CFLAGS -Wunused -Werror" +AM_CPPFLAGS="$AM_CPPFLAGS $glib_CFLAGS" +LIBS="$glib_LIBS $LIBS" + +AC_SUBST(AM_CFLAGS) +AC_SUBST(AM_CPPFLAGS) + +### Checks for library functions. +AC_CHECK_FUNCS([atexit memset strdup strstr]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/src/server/clients.c b/src/server/clients.c new file mode 100644 index 0000000..27a44d1 --- /dev/null +++ b/src/server/clients.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "server/clients.h" + +GSList *clients; + +pthread_mutex_t clients_lock; +pthread_mutexattr_t lock_attr; + +void +clients_init(void) +{ + // initialise recursive mutex + pthread_mutexattr_init(&lock_attr); + pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&clients_lock, &lock_attr); +} + +ChatClient* +clients_new(struct sockaddr_in client_addr, int socket) +{ + ChatClient *client = malloc(sizeof(ChatClient)); + client->ip = strdup(inet_ntoa(client_addr.sin_addr)); + client->port = ntohs(client_addr.sin_port); + client->sock = socket; + client->nickname = NULL; + + return client; +} + +void +clients_add(ChatClient *client) +{ + pthread_mutex_lock(&clients_lock); + clients = g_slist_append(clients, client); + pthread_mutex_unlock(&clients_lock); +} + +void +clients_print_total(void) +{ + pthread_mutex_lock(&clients_lock); + printf("Connected clients: %d\n", g_slist_length(clients)); + pthread_mutex_unlock(&clients_lock); +} diff --git a/src/server/clients.h b/src/server/clients.h new file mode 100644 index 0000000..137d556 --- /dev/null +++ b/src/server/clients.h @@ -0,0 +1,23 @@ +#ifndef __H_CLIENTS +#define __H_CLIENTS + +#include + +typedef struct thread_client_t { + char *ip; + int port; + int sock; + char *nickname; +} ChatClient; + +void clients_init(void); + +ChatClient* clients_new(struct sockaddr_in client_addr, int socket); +void clients_add(ChatClient *client); + +void clients_print_total(void); +void clients_register(ChatClient *client, char *nickname); +void clients_broadcast_message(char *from, char *message); +void clients_end_session(ChatClient *client); + +#endif \ No newline at end of file diff --git a/src/server/server.c b/src/server/server.c new file mode 100644 index 0000000..01c3d55 --- /dev/null +++ b/src/server/server.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include + +#include "server/clients.h" + +#define STREAM_REQ "" +#define STREAM_RESP "" + +void* connection_handler(void *data) +{ + ChatClient *client = (ChatClient *)data; + int read_size; + + // client loop + while(1) { + char buf[2]; + memset(buf, 0, sizeof(buf)); + + // listen to client stream + GString *stream = g_string_new(""); + errno = 0; + gboolean stream_opened = FALSE; + while ((!stream_opened) && ((read_size = recv(client->sock, buf, 1, 0)) > 0)) { + g_string_append_len(stream, buf, read_size); + if (g_strcmp0(stream->str, STREAM_REQ) == 0) { + stream_opened = TRUE; + } + memset(buf, 0, sizeof(buf)); + } + + // error + if (read_size == -1) { + perror("Error receiving on connection"); + g_string_free(stream, TRUE); + break; + + // client closed + } else if (read_size == 0) { + printf("%s:%d - Client disconnected.\n", client->ip, client->port); + g_string_free(stream, TRUE); + break; + + } else { + printf("RECV: %s\n", stream->str); + fflush(stdout); + g_string_free(stream, TRUE); + + int sent = 0; + int to_send = strlen(STREAM_RESP); + char *marker = STREAM_RESP; + while (to_send > 0 && ((sent = write(client->sock, marker, to_send)) > 0)) { + to_send -= sent; + marker += sent; + } + + printf("SENT: %s\n", STREAM_RESP); + fflush(stdout); + + memset(buf, 0, sizeof(buf)); + + // listen to client stream + stream = g_string_new(""); + errno = 0; + while ((read_size = recv(client->sock, buf, 1, 0)) > 0) { + printf("%c", buf[0]); + fflush(stdout); + g_string_append_len(stream, buf, read_size); + memset(buf, 0, sizeof(buf)); + } + } + } + + return 0; +} + +int main(int argc , char *argv[]) +{ + int port = 0; + + GOptionEntry entries[] = + { + { "port", 'p', 0, G_OPTION_ARG_INT, &port, "Listen port", NULL }, + { NULL } + }; + + GError *error = NULL; + GOptionContext *context; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, entries, NULL); + if (!g_option_context_parse(context, &argc, &argv, &error)) { + g_print("%s\n", error->message); + g_option_context_free(context); + g_error_free(error); + return 1; + } + + g_option_context_free(context); + + if (port == 0) { + port = 5222; + } + + int listen_socket, client_socket; + int c, ret; + struct sockaddr_in server_addr, client_addr; + + if (argc == 2) { + port = atoi(argv[1]); + } + + printf("Starting on port: %d...\n", port); + + clients_init(); + + // create socket + errno = 0; + listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); // ipv4, tcp, ip + if (listen_socket == -1) { + perror("Could not create socket"); + return 0; + } + + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(port); + + // bind socket to port + errno = 0; + ret = bind(listen_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (ret == -1) { + perror("Bind failed"); + return 0; + } + + // set socket to listen mode + errno = 0; + ret = listen(listen_socket, 5); + if (ret == -1) { + perror("Listen failed"); + return 0; + } + puts("Waiting for incoming connections..."); + + // connection accept loop + while (1) { + c = sizeof(struct sockaddr_in); + errno = 0; + client_socket = accept(listen_socket, (struct sockaddr *)&client_addr, (socklen_t*)&c); + if (client_socket == -1) { + perror("Accept failed"); + } + + // create thread for each new client + ChatClient *client = clients_new(client_addr, client_socket); + clients_add(client); + + pthread_t client_thread; + ret = pthread_create(&client_thread, NULL, connection_handler, (void *)client); + if (ret == 0) { + printf("%s:%d - Connection handler assigned.\n", client->ip, client->port); + } else { + puts("Could not create thread."); + } + + clients_print_total(); + } + + return 0; +}