aboutsummaryrefslogtreecommitdiff
path: root/server.c
blob: 1a24bcc1cc599a916103616bd987357925c23f46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Server for chatty
#include "common.h"
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define MAX_CONNECTIONS 5
#define FD_MAX MAX_CONNECTIONS + 1

static const char *filename = "history.dat";

enum { FD_SERVER = 0 };
u32 serverfd;

void err_exit(const char *msg)
{
    if (serverfd)
        if (close(serverfd))
            writef("Error while closing server socket. errno: %d\n", errno);
    fprintf(stderr, "%s errno: %d\n", msg, errno);
    _exit(1);
}

int main(void)
{
    u32 clientfd;
    u16 nclient             = 0;
    u32 on                  = 1;
    struct message msg_recv = {0};

    serverfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
    if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)))
        err_exit("Error while setting socket option.");

    const struct sockaddr_in address = {
        AF_INET,
        htons(PORT),
        {0},
    };

    if (bind(serverfd, (struct sockaddr *)&address, sizeof(address)))
        err_exit("Error while binding.");

    if (listen(serverfd, BUF_MAX))
        err_exit("Error while listening");

    writef("Listening on localhost:%d\n", PORT);

    struct pollfd fds[MAX_CONNECTIONS + 1] = {
        {serverfd, POLLIN, 0}, // FD_SERVER
        {      -1, POLLIN, 0},
        {      -1, POLLIN, 0},
        {      -1, POLLIN, 0},
        {      -1, POLLIN, 0},
        {      -1, POLLIN, 0},
    };

    for (;;) {
        u32 ret = poll(fds, FD_MAX, 50000);
        if (ret == -1)
            err_exit("Error while polling");
        else if (ret == 0) {
            writef("polling timed out.\n");
            continue;
        }

        // New client tries to connect to serverfd
        if (fds[FD_SERVER].revents & POLLIN) {
            clientfd = accept(serverfd, NULL, NULL);

            // When MAX_CONNECTIONS is reached, close new clients trying to connect.
            if (nclient == MAX_CONNECTIONS) {
                writef("Max connections reached.\n");
                if (send(clientfd, 0, 0, 0) == -1)
                    err_exit("Error while sending EOF to client socket.");
                if (shutdown(clientfd, SHUT_RDWR))
                    err_exit("Error while shutting down client socket.");
                if (close(clientfd))
                    err_exit("Error while closing client socket.");
            } else if (clientfd != -1) {
                nclient++;

                // get a new available spot in the fds array
                u32 i;
                for (i = 0; i < MAX_CONNECTIONS; i++)
                    if (fds[i].fd == -1)
                        break;
                fds[i].fd = clientfd;
                writef("New client: %d, %d\n", i, clientfd);

            } else {
                writef("Could not accept client errno: %d\n", errno);
            }
        }

        // Check for events on connected clients
        for (u32 i = 1; i <= nclient; i++) {
            if (!(fds[i].revents & POLLIN))
                continue;

            u32 nrecv;

            clientfd = fds[i].fd;

            nrecv = receive_message(&msg_recv, clientfd);
            if (nrecv == 0) {
                printf("client %d disconnected.\n", i);
                fds[i].fd      = -1;
                fds[i].revents = 0;
                if (shutdown(clientfd, SHUT_RDWR))
                    err_exit("Error while shutting down client %d socket.");
                if (close(clientfd))
                    err_exit("Error while cloing client socket.");
                nclient--;
                break;
            } else if (nrecv == -1) {
                // TODO: this can happen when connect is reset by pear
                err_exit("Error while receiving from client socket.");
            }

            // TODO:
            for (u32 j = 1; j <= nclient; j++) {
                // skip the client that sent the message
                if (j == i)
                    continue;
                if (send(fds[j].fd, &msg_recv, nrecv, 0) == -1)
                    printf("Error while sendig message to client %d. errno: %d\n", j, errno);
                else
                    printf("Retransmitted message to client %d.\n", j);
            }

            // // TODO: Serialize received message
            // FILE *f = fopen(filename, "wb");
            // save_message(&msg_recv, f);
            // fclose(f);
            // // return 0;

        }
    }

    return 0;
}