-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathserver.c
173 lines (155 loc) · 5.25 KB
/
server.c
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
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "achelper/ac_log.h"
#include "achelper/ac_memory.h"
#include "achelper/ac_protobuf.h"
#include "lib/client.h"
// do not sort
#include "lib/auth.h"
#include "lib/file.h"
#include "lib/socket.h"
#include "serverlib/command.h"
#define MAX_EVENTS 16
#define MAX_CLIENTS 256
#define LOUGOUT_CHECK_INTERVAL 1
bool IS_SERVER = true;
struct im_client *clients[MAX_CLIENTS] = {NULL};
int nclients = 0;
int epollfd;
int masterfd;
UserDb *db;
trie_file_t *fileDb;
void *time_thread(void *arg) {
struct IMResponse *rsp = encodeExitTextToIMResponse(
"You haven't issued any command for a while. You are automatically "
"logged off.",
false);
while (true) {
sleep(LOUGOUT_CHECK_INTERVAL);
for (int i = 0; i < nclients; i++) {
if (clients[i]->user != NULL && (!isUserLoggedIn(db, clients[i]->user))) {
logoutUser(db, epollfd, clients[i]->user);
send_response_to_client(epollfd, clients[i], rsp);
}
}
}
return NULL;
}
void *network_thread(void *arg) {
struct epoll_event *events =
ac_malloc(MAX_EVENTS * sizeof(struct epoll_event), "epoll events");
memset(events, 0, MAX_EVENTS * sizeof(struct epoll_event));
while (true) {
int N = epoll_wait(epollfd, events, MAX_EVENTS, -1);
ac_log(AC_LOG_DEBUG, "epoll_wait returned %d", N);
if (N == -1) break;
for (int i = 0; i < N; i++) {
if (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
int j = pick_client(clients, events[i].data.fd, &nclients, false);
if (j < 0) {
ac_log(AC_LOG_ERROR, "couldn't find im_client for fd %d",
events[i].data.fd);
break;
}
close_socket(epollfd, db, clients[j]);
} else if (events[i].data.fd == masterfd) {
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int newsockfd =
accept(masterfd, (struct sockaddr *)&client_addr, &client_addr_len);
ac_log(AC_LOG_DEBUG, "new socket %d", newsockfd);
if (newsockfd < 0) {
ac_log(AC_LOG_ERROR, "couldn't accept connection");
continue;
}
make_socket_nonblocking(newsockfd);
clients[pick_client(clients, newsockfd, &nclients, true)] =
im_connection_accept(epollfd, newsockfd, client_addr);
ac_log(AC_LOG_DEBUG, "done - new socket %d", newsockfd);
} else {
if (events[i].events & EPOLLIN) {
ac_log(AC_LOG_DEBUG, "epollin");
int j = pick_client(clients, events[i].data.fd, &nclients, false);
if (j < 0) {
ac_log(AC_LOG_ERROR, "couldn't find im_client for fd %d",
events[i].data.fd);
break;
}
if (clients[j]->fd == events[i].data.fd) {
ac_log(AC_LOG_DEBUG, "calling im_receive_command");
im_receive_command(epollfd, db, clients[j], events + i,
parse_command);
break;
}
}
if (events[i].events & EPOLLOUT) {
ac_log(AC_LOG_DEBUG, "epollout");
int j = pick_client(clients, events[i].data.fd, &nclients, false);
if (j < 0) {
ac_log(AC_LOG_ERROR, "couldn't find im_client for fd %d",
events[i].data.fd);
break;
}
if (clients[j]->fd == events[i].data.fd) {
pthread_mutex_lock(&(clients[j]->lock));
im_send_buffer(epollfd, db, clients[j], &(clients[j]->outbuffer));
int ct = (int)time(NULL);
if (clients[j]->user != NULL &&
clients[j]->user->last_active + db->login_timeout > ct)
im_send_buffer(epollfd, db, clients[j],
&(clients[j]->user->buffer));
pthread_mutex_unlock(&(clients[j]->lock));
break;
}
}
}
}
}
return NULL;
}
int main(int argc, char *argv[]) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
if (argc < 4) {
ac_log(AC_LOG_FATAL, "usage: ./server server_port block_duration timeout");
}
int port = atoi(argv[1]);
int block_duration = atoi(argv[2]);
int timeout = atoi(argv[3]);
FILE *udbfp = fopen("credentials.txt", "r");
if (udbfp == NULL) {
ac_log(AC_LOG_FATAL, "couldn't open credentials.txt");
}
db = buildUserDb(udbfp, block_duration, timeout);
fclose(udbfp);
fileDb = newTrieFileNode();
masterfd = listen_socket(port);
epollfd = epoll_create1(0);
if (epollfd < 0) {
ac_log(AC_LOG_FATAL, "couldn't create epoll: %s", strerror(errno));
}
struct epoll_event accept_event;
accept_event.data.fd = masterfd;
accept_event.events = EPOLLIN | EPOLLET;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, masterfd, &accept_event) < 0) {
ac_log(AC_LOG_FATAL, "epoll_ctl EPOLL_CTL_ADD error: %s", strerror(errno));
}
pthread_t nt, tt;
pthread_create(&nt, NULL, &network_thread, NULL);
pthread_create(&tt, NULL, &time_thread, NULL);
pthread_join(nt, NULL);
pthread_join(tt, NULL);
}