summaryrefslogtreecommitdiff
path: root/bittorrent/epoll_server
diff options
context:
space:
mode:
Diffstat (limited to 'bittorrent/epoll_server')
-rw-r--r--bittorrent/epoll_server/connection.c58
-rw-r--r--bittorrent/epoll_server/connection.h54
-rw-r--r--bittorrent/epoll_server/epoll_server.c134
-rw-r--r--bittorrent/epoll_server/epoll_server.h58
-rw-r--r--bittorrent/epoll_server/meson.build32
-rw-r--r--bittorrent/epoll_server/utils/xalloc.c31
-rw-r--r--bittorrent/epoll_server/utils/xalloc.h32
7 files changed, 399 insertions, 0 deletions
diff --git a/bittorrent/epoll_server/connection.c b/bittorrent/epoll_server/connection.c
new file mode 100644
index 0000000..62560c5
--- /dev/null
+++ b/bittorrent/epoll_server/connection.c
@@ -0,0 +1,58 @@
+#include "connection.h"
+
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "utils/xalloc.h"
+
+struct connection_t *add_client(struct connection_t *head, int client_socket)
+{
+ struct connection_t *new_connection = xmalloc(sizeof(struct connection_t));
+
+ new_connection->client_socket = client_socket;
+ new_connection->buffer = NULL;
+ new_connection->nb_read = 0;
+ new_connection->next = head;
+
+ return new_connection;
+}
+
+struct connection_t *remove_client(struct connection_t *head, int client_socket)
+{
+ if (head && head->client_socket == client_socket)
+ {
+ struct connection_t *client_connection = head->next;
+ if (close(head->client_socket) == -1)
+ err(EXIT_FAILURE, "Failed to close socket");
+ free(head->buffer);
+ free(head);
+ return client_connection;
+ }
+
+ struct connection_t *tmp = head;
+ while (tmp->next)
+ {
+ if (tmp->next->client_socket == client_socket)
+ {
+ struct connection_t *client_connection = tmp->next;
+ tmp->next = client_connection->next;
+ if (close(client_connection->client_socket) == -1)
+ err(EXIT_FAILURE, "Failed to close socket");
+ free(client_connection->buffer);
+ free(client_connection);
+ break;
+ }
+ tmp = tmp->next;
+ }
+
+ return head;
+}
+
+struct connection_t *find_client(struct connection_t *head, int client_socket)
+{
+ while (head != NULL && head->client_socket != client_socket)
+ head = head->next;
+
+ return head;
+}
diff --git a/bittorrent/epoll_server/connection.h b/bittorrent/epoll_server/connection.h
new file mode 100644
index 0000000..40e2370
--- /dev/null
+++ b/bittorrent/epoll_server/connection.h
@@ -0,0 +1,54 @@
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include <sys/types.h>
+
+/**
+ * \brief Contains the information about the clients (linked list)
+ */
+struct connection_t
+{
+ int client_socket; /**< socket fd of the client */
+
+ char *buffer; /**< buffer containing the data received by this client */
+
+ ssize_t nb_read; /**< number of bytes read (also size of the buffer) */
+
+ struct connection_t *next; /**< the next client */
+};
+
+/**
+ * \brief Adds a new client connection_t to the linked list
+ *
+ * \param connection: the connection_t linked list with all the clients
+ *
+ * \param client_socket: the client socket fd to add
+ *
+ * \return The connection_t linked list with the element added
+ */
+struct connection_t *add_client(struct connection_t *head, int client_socket);
+
+/**
+ * \brief Removes the client connection_t from the linked list connection
+ *
+ * \param connection: the connection_t linked list with all the clients
+ *
+ * \param client_socket: the client socket fd to remove
+ *
+ * \return The connection_t linked list with element removed
+ */
+struct connection_t *remove_client(struct connection_t *head,
+ int client_socket);
+
+/**
+ * \brief Find the connection_t element where the socket is equal to client sock
+ *
+ * \param connection: the connection_t linked list with all the clients
+ *
+ * \param client_socket: the client socket to find
+ *
+ * \return The connection_t element of the specific client
+ */
+struct connection_t *find_client(struct connection_t *head, int client_socket);
+
+#endif /* !CONNECTION_H */
diff --git a/bittorrent/epoll_server/epoll_server.c b/bittorrent/epoll_server/epoll_server.c
new file mode 100644
index 0000000..dc010e6
--- /dev/null
+++ b/bittorrent/epoll_server/epoll_server.c
@@ -0,0 +1,134 @@
+#include "epoll_server.h"
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "connection.h"
+#include "string.h"
+
+int create_and_bind(struct addrinfo *addrinfo)
+{
+ for (struct addrinfo *info = addrinfo; info != NULL; info = info->ai_next)
+ {
+ int sock_fd =
+ socket(info->ai_family, info->ai_socktype, info->ai_protocol);
+ if (sock_fd != -1)
+ {
+ int feur = bind(sock_fd, info->ai_addr, info->ai_addrlen);
+ if (feur != -1)
+ {
+ return sock_fd;
+ }
+ else
+ {
+ close(sock_fd);
+ }
+ }
+ }
+ exit(1);
+}
+
+int prepare_socket(const char *ip, const char *port)
+{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ struct addrinfo *addrinfo;
+ int e = getaddrinfo(ip, port, &hints, &addrinfo);
+ if (e != 0)
+ {
+ exit(1);
+ }
+ e = create_and_bind(addrinfo);
+ int e2 = listen(e, 1000);
+ freeaddrinfo(addrinfo);
+ if (e2 == -1)
+ {
+ close(e);
+ exit(1);
+ }
+ return e;
+}
+
+struct connection_t *accept_client(int epoll_instance, int server_socket,
+ struct connection_t *connection)
+{
+ int e = accept(server_socket, NULL, 0);
+ if (e == -1)
+ {
+ close(server_socket);
+ exit(1);
+ }
+ struct epoll_event tmp;
+ tmp.events = EPOLLIN;
+ tmp.data.fd = e;
+ int er = epoll_ctl(epoll_instance, EPOLL_CTL_ADD, e, &tmp);
+ if (er == -1)
+ {
+ close(server_socket);
+ close(e);
+ exit(1);
+ }
+ return add_client(connection, e);
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 3)
+ {
+ exit(1);
+ }
+ int epoll_instance = epoll_create1(0);
+ if (epoll_instance == -1)
+ {
+ exit(1);
+ }
+ int e = prepare_socket(argv[1], argv[2]);
+ struct connection_t *co = NULL;
+ struct epoll_event tmp;
+ tmp.events = EPOLLIN;
+ tmp.data.fd = e;
+ if (epoll_ctl(epoll_instance, EPOLL_CTL_ADD, e, &tmp) == -1)
+ {
+ close(e);
+ exit(1);
+ }
+ while (1)
+ {
+ struct epoll_event elist[MAX_EVENTS];
+ int info = epoll_wait(epoll_instance, elist, MAX_EVENTS, -1);
+ if (info == -1)
+ {
+ exit(1);
+ }
+ for (int i = 0; i < info; i++)
+ {
+ int fd = elist[i].data.fd;
+ if (fd == e)
+ {
+ co = accept_client(epoll_instance, e, co);
+ }
+ else
+ {
+ char buf[100] = { 0 };
+ int info1 = recv(fd, buf, 100, 0);
+ if (info1 < 1)
+ {
+ co = remove_client(co, fd);
+ close(fd);
+ }
+ else
+ {
+ struct connection_t *tmp = co;
+ while (tmp != NULL)
+ {
+ send(tmp->client_socket, buf, info1, 0);
+ tmp = tmp->next;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/bittorrent/epoll_server/epoll_server.h b/bittorrent/epoll_server/epoll_server.h
new file mode 100644
index 0000000..ba5de4f
--- /dev/null
+++ b/bittorrent/epoll_server/epoll_server.h
@@ -0,0 +1,58 @@
+#ifndef EPOLL_SERVER_H
+#define EPOLL_SERVER_H
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "connection.h"
+
+/**
+ * \brief The length of the event array, must be greater than zero
+ */
+#define MAX_EVENTS 64
+
+#define DEFAULT_BUFFER_SIZE 2048
+
+/**
+ * \brief Iterate over the struct addrinfo elements to create and bind a socket
+ *
+ * \param addrinfo: struct addrinfo elements
+ *
+ * \return The created socket or exit with 1 if there is an error
+ *
+ * Try to create and connect a socket with every addrinfo element until it
+ * succeeds
+ *
+ */
+int create_and_bind(struct addrinfo *addrinfo);
+
+/**
+ * \brief Initialize the Addrinfo struct and call create_and bind() and
+ * listen(2)
+ *
+ * \param ip: IP address of the server
+ * \param port: Port of the server
+ *
+ * \return The created socket
+ *
+ * Initialize the struct addrinfo needed by create_and_bind() before calling
+ * it. When create_and_bind() returns a valid socket, set the socket to
+ * listening and return it.
+ */
+int prepare_socket(const char *ip, const char *port);
+
+/**
+ * \brief Accept a new client and add it to the connection_t struct
+ *
+ * \param epoll_instance: the epoll instance
+ * \param server_socket: listening socket
+ * \param connection: the connection linked list with all the current
+ * connections
+ *
+ * \return The connection struct with the new client added
+ */
+struct connection_t *accept_client(int epoll_instance, int server_socket,
+ struct connection_t *connection);
+
+#endif /* !EPOLL_SERVER_H */
diff --git a/bittorrent/epoll_server/meson.build b/bittorrent/epoll_server/meson.build
new file mode 100644
index 0000000..de9bba1
--- /dev/null
+++ b/bittorrent/epoll_server/meson.build
@@ -0,0 +1,32 @@
+project(
+ 'epoll_server',
+ 'c',
+ version : '1.0.0',
+ default_options : [
+ 'debug=true',
+ 'c_std=c99',
+ 'buildtype=debug',
+ 'warning_level=2',
+ 'werror=true',
+ 'b_sanitize=address,undefined',
+ 'optimization=plain',
+ ],
+)
+
+deps = []
+pubinc = []
+inc = [include_directories('./')]
+src = files(
+ 'connection.c',
+ 'epoll_server.c',
+ 'utils/xalloc.c'
+)
+
+executable(
+ 'epoll_server',
+ sources : src,
+ include_directories : pubinc + inc,
+ dependencies : deps,
+ c_args : '-D_DEFAULT_SOURCE',
+ install : true,
+)
diff --git a/bittorrent/epoll_server/utils/xalloc.c b/bittorrent/epoll_server/utils/xalloc.c
new file mode 100644
index 0000000..e4dc6b8
--- /dev/null
+++ b/bittorrent/epoll_server/utils/xalloc.c
@@ -0,0 +1,31 @@
+#include "xalloc.h"
+
+#include <err.h>
+#include <stdlib.h>
+
+void *xmalloc(size_t size)
+{
+ void *res = malloc(size);
+ if (!res)
+ err(EXIT_FAILURE, "Impossible to malloc");
+
+ return res;
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+ void *res = calloc(nmemb, size);
+ if (!res)
+ err(EXIT_FAILURE, "Impossible to calloc");
+
+ return res;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+ void *res = realloc(ptr, size);
+ if (!res)
+ err(EXIT_FAILURE, "Impossible to realloc");
+
+ return res;
+}
diff --git a/bittorrent/epoll_server/utils/xalloc.h b/bittorrent/epoll_server/utils/xalloc.h
new file mode 100644
index 0000000..e7655b8
--- /dev/null
+++ b/bittorrent/epoll_server/utils/xalloc.h
@@ -0,0 +1,32 @@
+#ifndef XALLOC_H
+#define XALLOC_H
+
+#include <stddef.h>
+
+/**
+** \brief Malloc wrapper that exits on failure.
+**
+** \param size The size to malloc.
+** \return The malloc return.
+*/
+void *xmalloc(size_t size);
+
+/**
+** \brief Calloc wrapper that exits on failure.
+**
+** \param nmemb The number of elements.
+** \param size The size of an element.
+** \return The calloc return.
+*/
+void *xcalloc(size_t nmemb, size_t size);
+
+/**
+** \brief Realloc wrapper that exits on failure.
+**
+** \param ptr The mem pointer.
+** \param size The size to realloc.
+** \return The realloc return.
+*/
+void *xrealloc(void *ptr, size_t size);
+
+#endif /* !XALLOC_H */