aboutsummaryrefslogtreecommitdiff
path: root/server.c
blob: 675bfb70bc25d8c5950ff5bf46b2fdad24049e90 (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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Server for chatty

// The idea is the following:
// - tcp server that you can send messages to
// - encrypted communication
// - history upon connecting
// - date of messages sent
// - author
// - fingeprint as ID for authorship
// - client for reading the messages and sending them at the same time

// TODO:
// - min height & width
// - wrapping input
// - max y for new messages and make them scroll
// - check resize event
// - asynchronously receive/send a message
// - fix receiving messages with arbitrary text length

// TODO: send message to all other clients
// - implement different rooms
// - implement history
// - implement tls

#include "common.h"
#include "config.h"
#include <arpa/inet.h>
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.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 };
int 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)
{
    int clientfd;
    int nclient             = 0;
    int 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 (;;) {
        int 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
                int 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 (int i = 1; i <= nclient; i++) {
            if (!(fds[i].revents & POLLIN))
                continue;

            int nrecv;

            clientfd = fds[i].fd;
            nrecv    = recv(clientfd, &msg_recv, sizeof(struct message), 0);

            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) {
                err_exit("Error while receiving from client socket.");
            }

            printf("client %d sent %d bytes.\n", i, nrecv);

            // TODO:
            for (int 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: check if bytes are correct
            // FILE *f = fopen("srv_recv.bin", "wb");
            // fwrite(&msg_recv, sizeof(struct message), 1, f);
            // fclose(f);
            //
            // printf("written %lu bytes to srv_recv.bin\n", sizeof(msg_recv));
            // return 0;
        }
    }

    return 0;
}