Kajetan Johannes Hammerle 5 years ago
commit
8218c8041f
8 changed files with 631 additions and 0 deletions
  1. 3 0
      .gitignore
  2. 25 0
      Makefile
  3. 297 0
      Server.c
  4. 35 0
      Server.h
  5. 55 0
      ServerMain.c
  6. 72 0
      Stream.c
  7. 24 0
      Stream.h
  8. 120 0
      client.c

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+nbproject/
+client
+server

+ 25 - 0
Makefile

@@ -0,0 +1,25 @@
+FLAGS = -std=c11 -Wall
+SERVER_LINKER = -lpthread
+
+all: server client
+	
+run_all: all
+	./server
+	./client 127.0.0.1
+
+server: ServerMain.c Server.c Server.h Stream.c Stream.h
+	gcc $(FLAGS) -o $@ ServerMain.c Server.c Stream.c $(SERVER_LINKER)
+
+run_server: server
+	./server
+
+
+client: client.c Stream.c Stream.h
+	gcc $(FLAGS) -o $@ client.c Stream.c
+
+run_client: client
+	./client 127.0.0.1
+		
+	
+clean:
+	rm -f client server

+ 297 - 0
Server.c

@@ -0,0 +1,297 @@
+#include "Server.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>  
+#include "Stream.h" 
+
+void serverInitDefaults(Server* s)
+{
+    s->maxClients = -1;
+    s->port = -1;
+    s->threads = NULL;
+    s->clientSockets = NULL;
+    s->connectSocket = -1;
+    
+    s->hAmount = -1;
+    s->hIndex = -1;
+    s->handlers = NULL;
+}
+
+int serverInit(Server* s, int maxClients, short port)
+{
+    s->maxClients = maxClients;
+    s->port = port;
+    s->hAmount = 0;
+    s->hIndex = 0;
+    
+    // initialize storage for clients
+    s->threads = malloc(sizeof(pthread_t) * maxClients);
+    s->clientSockets = malloc(sizeof(int) * maxClients);
+    
+    for(int i = 0; i < maxClients; i++)
+    {
+        s->threads[i] = -1;
+        s->clientSockets[i] = -1;
+    }
+    
+    // create the socket for clients to connect
+    s->connectSocket = socket(AF_INET, SOCK_STREAM, 0);
+    if(s->connectSocket == -1)
+    {
+        perror("Cannot create socket");
+        serverRemove(s);
+        return -1;
+    }
+    
+    // prevents clients from blocking the port if the server exits
+    struct linger sl;
+    sl.l_onoff = 1;
+    sl.l_linger = 0;
+    if(setsockopt(s->connectSocket, SOL_SOCKET, SO_LINGER, &sl, sizeof(struct linger)) == -1)
+    {
+        perror("Cannot set non lingering");
+        serverRemove(s);
+        return -1;
+    }
+    
+    // specifies data of the port and binds it
+    struct sockaddr_in connectSocketData;
+    memset(&connectSocketData, 0, sizeof(struct sockaddr_in));
+    connectSocketData.sin_family = AF_INET;
+    connectSocketData.sin_addr.s_addr = INADDR_ANY;
+    connectSocketData.sin_port = htons(port);
+
+    if(bind(s->connectSocket, (struct sockaddr*) &connectSocketData, sizeof(struct sockaddr_in)) != 0)
+    {
+        perror("Cannot bind socket");
+        serverRemove(s);
+        return -1;
+    }
+    
+    // mark this socket as handler for connection requests
+    if(listen(s->connectSocket, 5) != 0)
+    {
+        perror("Cannot start listening");
+        serverRemove(s);
+        return -1;
+    }
+    
+    return 0;
+}
+
+void serverRemove(Server* s)
+{
+    s->port = -1;
+    
+    if(s->threads != NULL)
+    {
+        for(int i = 0; i < s->maxClients; i++)
+        {
+            if(s->threads[i] != -1)
+            {
+                printf("%d thread\n", i);
+                pthread_cancel(s->threads[i]);
+                pthread_join(s->threads[i], NULL);
+            }
+        }
+        free(s->threads);
+        s->threads = NULL;
+    }
+    
+    if(s->clientSockets != NULL)
+    {
+        for(int i = 0; i < s->maxClients; i++)
+        {
+            if(s->clientSockets[i] != -1)
+            {
+                if(close(s->clientSockets[i]) == -1)
+                {
+                    printf("%d", i);
+                    perror(" cannot close client socket");
+                }
+                else
+                {
+                    printf("%d closed\n", i);
+                }
+            }
+        }
+        free(s->clientSockets);
+        s->clientSockets = NULL;
+    }
+    
+    s->maxClients = -1;
+    
+    if(s->connectSocket != -1)
+    {
+        if(close(s->connectSocket) != 0)
+        {
+            perror("Cannot close server socket");
+        }
+        else
+        {
+            printf("connection listener closed\n");
+        }
+        s->connectSocket = -1;
+    }
+    
+    s->hAmount = -1;
+    s->hIndex = -1;
+    if(s->handlers != NULL)
+    {
+        free(s->handlers);
+        s->handlers = NULL;
+    }
+}
+
+void* clientHandler(void* data)
+{
+    int id = ((ThreadData*) data)->id;
+    Server* s = ((ThreadData*) data)->server;
+    
+    Stream in;
+    while(strcmp(in.data, "quit") != 0)
+    {
+        int size = recv(s->clientSockets[id], in.data, BUFFER_SIZE - 1, 0);
+        if(size > 0)
+        {
+            in.data[size] = '\0';
+            int package = ((int) in.data[0]);
+            if(package >= 0 && package < s->hIndex)
+            {
+                printf("Received package %d from %d: %s\n", package, id, in.data);
+                in.index = 1;
+                in.size = size;
+                s->handlers[package](&in);
+            }
+            else
+            {
+                printf("Invalid package %d from %d\n", package, id);
+            }
+        }
+        else if(size == 0)
+        {
+            printf("Client %d closed remote socket\n", id);
+            break;
+        }   
+        else
+        {
+            perror("recv error");
+            break;
+        }
+    }
+    
+    if(close(s->clientSockets[id]) != 0)
+    {
+        printf("%d", id);
+        perror(" cannot close client socket");
+    }
+    else
+    {
+        printf("%d closed\n", id);
+    }
+    
+    s->clientSockets[id] = -1;
+    return NULL;
+}
+
+void serverWaitForConnection(Server* s)
+{
+    socklen_t addrlen = sizeof(struct sockaddr_in);
+    char buffer[BUFFER_SIZE];
+    
+    while(1)
+    {
+        printf("Waiting for connections...\n");
+        struct sockaddr_in clientSocketData;
+        int clientSocket = accept(s->connectSocket, (struct sockaddr*) &clientSocketData, &addrlen);
+        if(clientSocket >= 0)
+        {
+            printf("Client connected from %s:%d...\n", inet_ntoa(clientSocketData.sin_addr), (int) ntohs(clientSocketData.sin_port));
+            
+            int i = 0;
+            while(1)
+            {
+                if(s->clientSockets[i] == -1)
+                {
+                    if(s->threads[i] != -1)
+                    {
+                        pthread_cancel(s->threads[i]);
+                        pthread_join(s->threads[i], NULL);
+                    }
+                    
+                    ThreadData data;
+                    data.id = i;
+                    data.server = s;
+                    if(pthread_create(&s->threads[i], NULL, clientHandler, (void*) &data) != 0)
+                    {
+                        perror("Cannot create thread");  
+                        
+                        strcpy(buffer, "error: cannot create thread\n");
+                        if(send(clientSocket, buffer, strlen(buffer), 0) == -1)
+                        {
+                            perror("Cannot send error");
+                        }
+                        close(clientSocket);
+                    }
+                    else
+                    {
+                        s->clientSockets[i] = clientSocket;
+                        
+                        strcpy(buffer, "Welcome to server, please enter your command:\n");
+                        if(send(clientSocket, buffer, strlen(buffer), 0) == -1)
+                        {
+                            perror("Cannot send welcome message");
+                            close(clientSocket);
+                            s->clientSockets[i] = -1;
+                        }
+                    }
+                    break;
+                }
+                
+                i++;
+                if(i >= s->maxClients)
+                {
+                    printf("max clients reached\n");    
+                    
+                    strcpy(buffer, "error: max clients reached\n");
+                    if(send(clientSocket, buffer, strlen(buffer), 0) == -1)
+                    {
+                        perror("Cannot send error");
+                    }
+                    close(clientSocket);
+                    break;
+                }
+            }
+        }
+    }
+}
+
+void serverRegisterHandler(Server* s, StreamFunction f)
+{
+    if(s->hAmount == s->hIndex)
+    {
+        if(s->hAmount == 0)
+        {
+            s->handlers = malloc(sizeof(StreamFunction));
+            s->hAmount = 1;
+        }
+        else
+        {
+            s->hAmount *= 2;
+            StreamFunction* functions = malloc(sizeof(StreamFunction) * s->hAmount);
+            for(int i = 0; i < s->hIndex; i++)
+            {
+                functions[i] = s->handlers[i];
+            }
+            free(s->handlers);
+            s->handlers = functions;
+        }
+    }
+    s->handlers[s->hIndex] = f;
+    s->hIndex++;
+}

+ 35 - 0
Server.h

@@ -0,0 +1,35 @@
+#ifndef SERVER_H
+#define SERVER_H
+
+#include <pthread.h>
+#include "Stream.h"
+
+typedef struct Server
+{
+    int maxClients;
+    short port;
+    pthread_t* threads;
+    int* clientSockets;
+    int connectSocket;
+    
+    int hAmount;
+    int hIndex;
+    StreamFunction* handlers;
+} Server;
+
+typedef struct ThreadData
+{
+    int id;
+    Server* server;
+} ThreadData;
+
+void serverInitDefaults(Server* s);
+int serverInit(Server* s, int maxClients, short port);
+void serverRemove(Server* s);
+
+void serverWaitForConnection(Server* s);
+
+void serverRegisterHandler(Server* s, StreamFunction f);
+
+#endif
+

+ 55 - 0
ServerMain.c

@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "Server.h"
+
+Server server;
+
+void interruptHandler(int signal)
+{
+    serverRemove(&server);
+    exit(EXIT_SUCCESS);
+}
+
+void test(Stream* in)
+{
+    int i;
+    while(streamGetInt(&i, in) != -1)
+    {
+        printf("%d  ", i);
+    }
+    printf(" HALLO\n");
+}
+
+void test2(Stream* in)
+{
+    printf("HALLO2\n");
+}
+
+void test3(Stream* in)
+{
+    printf("HALLO3\n");
+}
+
+int main()
+{
+    serverInitDefaults(&server);
+    
+    signal(SIGINT, interruptHandler);
+    signal(SIGKILL, interruptHandler);
+    
+    if(serverInit(&server, 3, 6543))
+    {
+        return EXIT_FAILURE;
+    }
+    
+    serverRegisterHandler(&server, test);
+    serverRegisterHandler(&server, test2);
+    serverRegisterHandler(&server, test3);
+    
+    serverWaitForConnection(&server);
+    
+    serverRemove(&server);
+    return EXIT_SUCCESS;
+}

+ 72 - 0
Stream.c

@@ -0,0 +1,72 @@
+#include "Stream.h"
+
+int streamGetChar(char* c, Stream* in)
+{
+    if(in->index < in->size)
+    {
+        *c = in->data[in->index];
+        in->index++;
+        return 0;
+    }
+    return -1;
+}
+
+int streamGetShort(short* s, Stream* in)
+{
+    if(in->index + 1 < in->size)
+    {
+        *s = (in->data[in->index] & 0xFF) | ((in->data[in->index + 1] & 0xFF) << 8);
+        in->index += 2;
+        return 0;
+    }
+    return -1;
+}
+
+int streamGetInt(int* i, Stream* in)
+{
+    if(in->index + 3 < in->size)
+    {
+        *i = (in->data[in->index] & 0xFF) | ((in->data[in->index + 1] & 0xFF) << 8) | 
+                ((in->data[in->index + 2] & 0xFF) << 16) | ((in->data[in->index + 3] & 0xFF) << 24);
+        in->index += 4;
+        return 0;
+    }
+    return -1;
+}
+
+int streamWriteChar(char c, Stream* in)
+{
+    if(in->index < in->size)
+    {
+        in->data[in->index] = c;
+        in->index++;
+        return 0;
+    }
+    return -1;
+}
+
+int streamWriteShort(short s, Stream* in)
+{
+    if(in->index + 1 < in->size)
+    {
+        in->data[in->index] = s & 0xFF;
+        in->data[in->index + 1] = (s >> 8) & 0xFF;
+        in->index += 2;
+        return 0;
+    }
+    return -1;
+}
+
+int streamWriteInt(int i, Stream* in)
+{
+    if(in->index + 3 < in->size)
+    {
+        in->data[in->index] = i & 0xFF;
+        in->data[in->index + 1] = (i >> 8) & 0xFF;
+        in->data[in->index + 2] = (i >> 16) & 0xFF;
+        in->data[in->index + 3] = (i >> 24) & 0xFF;
+        in->index += 4;
+        return 0;
+    }
+    return -1;
+}

+ 24 - 0
Stream.h

@@ -0,0 +1,24 @@
+#ifndef STREAM_H
+#define STREAM_H
+
+#define BUFFER_SIZE 1024
+
+typedef struct Stream
+{
+    int size;
+    int index;
+    char data[BUFFER_SIZE];
+} Stream;
+
+typedef void (*StreamFunction) (Stream*);
+
+int streamGetChar(char* c, Stream* in);
+int streamGetShort(short* s, Stream* in);
+int streamGetInt(int* i, Stream* in);
+
+int streamWriteChar(char c, Stream* in);
+int streamWriteShort(short s, Stream* in);
+int streamWriteInt(int i, Stream* in);
+
+#endif
+

+ 120 - 0
client.c

@@ -0,0 +1,120 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#define __USE_MISC
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "Stream.h"
+
+#define BUFFER_SIZE 1024
+#define PORT 6543
+
+int clientSocket = -1;
+
+void cleanUp()
+{
+    if(clientSocket != -1)
+    {
+        if(close(clientSocket) != 0)
+        {
+            perror("Cannot close client socket");
+        }
+        else
+        {
+            printf("socket closed\n");
+        }
+    }
+}
+
+void interruptHandler(int signal)
+{
+    cleanUp();
+    exit(EXIT_SUCCESS);
+}
+
+void safeExit(int status)
+{
+    cleanUp();
+    exit(status);
+}
+
+int main(int argc, char **argv)
+{
+    signal(SIGINT, interruptHandler);
+    signal(SIGKILL, interruptHandler);
+
+    char buffer[BUFFER_SIZE];
+    int size;
+
+    if(argc < 2)
+    {
+        printf("Usage: %s server_address\n", argv[0]);
+        safeExit(EXIT_FAILURE);
+    }
+
+    clientSocket = socket(AF_INET, SOCK_STREAM, 0);
+    if(clientSocket == -1)
+    {
+        perror("Cannot create socket");
+        safeExit(EXIT_FAILURE);
+    }
+
+    struct sockaddr_in clientSocketData;
+    memset(&clientSocketData, 0, sizeof (clientSocketData));
+    clientSocketData.sin_family = AF_INET;
+    clientSocketData.sin_port = htons(PORT);
+    if(inet_aton(argv[1], &clientSocketData.sin_addr) == 0)
+    {
+        printf("'%s' is not a valid ip\n", argv[1]);
+        safeExit(EXIT_FAILURE);
+    }
+
+    if(connect(clientSocket, (struct sockaddr*) &clientSocketData, sizeof(clientSocketData)) == 0)
+    {
+        printf("Connection with server (%s) established\n", inet_ntoa(clientSocketData.sin_addr));
+        size = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
+        if(size > 0)
+        {
+            buffer[size] = '\0';
+            printf("%s", buffer);
+            if(strncmp(buffer, "error", 5) == 0)
+            {
+                safeExit(EXIT_FAILURE);
+            }
+        }
+    }
+    else
+    {
+        perror("No server is available");
+        safeExit(EXIT_FAILURE);
+    }
+
+    while(strcmp(buffer, "quit\n") != 0)
+    {
+        printf("Send message: ");
+        fgets(buffer, BUFFER_SIZE, stdin);
+        
+        Stream out;
+        out.index = 0;
+        out.size = BUFFER_SIZE;
+        streamWriteChar(0, &out);
+        streamWriteInt(-423, &out);
+        streamWriteInt(-6456463, &out);
+        streamWriteInt(443534, &out);
+        streamWriteInt(75676, &out);
+        
+        if(send(clientSocket, out.data, out.index, MSG_NOSIGNAL) == -1)
+        {
+            perror("Cannot send data");
+            break;
+        }
+    }
+    
+    safeExit(EXIT_SUCCESS);
+    return EXIT_SUCCESS;
+}