199 lines
5.3 KiB
C
199 lines
5.3 KiB
C
#include <fishsoup.h>
|
|
|
|
#include "stb_ds.h"
|
|
|
|
fishsoup_server_t* fishsoup_server(int port) {
|
|
fishsoup_server_t* server = malloc(sizeof(*server));
|
|
int val;
|
|
|
|
if(port == 0) port = FISHSOUP_PORT;
|
|
|
|
if((server->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
|
|
free(server);
|
|
return NULL;
|
|
}
|
|
|
|
val = 1;
|
|
setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&val, sizeof(val));
|
|
|
|
server->address.sin_family = AF_INET;
|
|
server->address.sin_addr.s_addr = INADDR_ANY;
|
|
server->address.sin_port = htons(port);
|
|
|
|
if(bind(server->fd, (struct sockaddr*)&server->address, sizeof(server->address)) < 0) {
|
|
close(server->fd);
|
|
free(server);
|
|
return NULL;
|
|
}
|
|
|
|
if(listen(server->fd, 128) < 0) {
|
|
close(server->fd);
|
|
free(server);
|
|
return NULL;
|
|
}
|
|
|
|
server->clients = NULL;
|
|
server->on_client = NULL;
|
|
server->on_tick = NULL;
|
|
|
|
return server;
|
|
}
|
|
|
|
int fishsoup_server_clients(fishsoup_server_t* server) {
|
|
return arrlen(server->clients);
|
|
}
|
|
|
|
void fishsoup_server_loop(fishsoup_server_t* server) {
|
|
struct pollfd* pfd;
|
|
|
|
while(1) {
|
|
int i;
|
|
int ret;
|
|
pfd = malloc(sizeof(*pfd) * (1 + arrlen(server->clients)));
|
|
|
|
pfd[0].fd = server->fd;
|
|
pfd[0].events = POLLIN | POLLPRI;
|
|
|
|
for(i = 0; i < arrlen(server->clients); i++) {
|
|
pfd[1 + i].fd = server->clients[i].fd;
|
|
pfd[1 + i].events = POLLIN | POLLPRI;
|
|
}
|
|
|
|
ret = poll(pfd, 1 + arrlen(server->clients), 50);
|
|
if(ret < 0) {
|
|
free(pfd);
|
|
break;
|
|
}
|
|
|
|
if(server->on_tick != NULL) server->on_tick(server);
|
|
|
|
if(pfd[0].revents & POLLIN) {
|
|
fishsoup_client_t client;
|
|
struct sockaddr_in address;
|
|
int len = sizeof(address);
|
|
|
|
client.fd = accept(server->fd, (struct sockaddr*)&address, &len);
|
|
client.last_ping = time(NULL);
|
|
client.server.cleanup = 0;
|
|
client.server.sent_ping = 0;
|
|
client.on_packet = NULL;
|
|
|
|
arrput(server->clients, client);
|
|
|
|
if(server->on_client != NULL) server->on_client(server, &server->clients[arrlen(server->clients) - 1]);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Client %d accepted\n", server->clients[i].fd);
|
|
#endif
|
|
}
|
|
|
|
for(i = 0; i < arrlen(server->clients); i++) {
|
|
time_t diff;
|
|
if(server->clients[i].server.cleanup) continue;
|
|
if(pfd[1 + i].revents & POLLIN) {
|
|
fishsoup_packet_t pkt;
|
|
|
|
if(fishsoup_packet_recv(server->clients[i].fd, &pkt) == 0) {
|
|
server->clients[i].server.cleanup = 1;
|
|
} else {
|
|
if(pkt.header.type == FISHSOUP_PACKET_PONG && server->clients[i].server.sent_ping) {
|
|
server->clients[i].last_ping = time(NULL);
|
|
server->clients[i].server.sent_ping = 0;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Pong from client %d\n", server->clients[i].fd);
|
|
#endif
|
|
} else if(pkt.header.type == FISHSOUP_PACKET_PING) {
|
|
fishsoup_packet_t ret;
|
|
|
|
ret.header.type = FISHSOUP_PACKET_PONG;
|
|
ret.header.length = 0;
|
|
ret.data = NULL;
|
|
|
|
if(fishsoup_packet_send(server->clients[i].fd, &ret) == 0) {
|
|
server->clients[i].server.cleanup = 1;
|
|
} else {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Ping from client %d, sending back pong\n", server->clients[i].fd);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if(!server->clients[i].server.cleanup && server->clients[i].on_packet != NULL) server->clients[i].on_packet(&server->clients[i], &pkt);
|
|
|
|
if(pkt.data != NULL) free(pkt.data);
|
|
}
|
|
}
|
|
if(server->clients[i].server.cleanup) continue;
|
|
|
|
diff = time(NULL) - server->clients[i].last_ping;
|
|
|
|
if(diff == FISHSOUP_PING && !server->clients[i].server.sent_ping) {
|
|
fishsoup_packet_t pkt;
|
|
|
|
pkt.header.type = FISHSOUP_PACKET_PING;
|
|
pkt.header.length = 0;
|
|
pkt.data = NULL;
|
|
|
|
if(fishsoup_packet_send(server->clients[i].fd, &pkt) == 0) {
|
|
server->clients[i].server.cleanup = 1;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Failed to ping client %d\n", server->clients[i].fd);
|
|
#endif
|
|
} else {
|
|
server->clients[i].server.sent_ping = 1;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Pinging client %d\n", server->clients[i].fd);
|
|
#endif
|
|
}
|
|
} else if(diff == FISHSOUP_PING * 2 && server->clients[i].server.sent_ping) {
|
|
server->clients[i].server.cleanup = 1;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Ping timeout (client %d)\n", server->clients[i].fd);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < arrlen(server->clients); i++) {
|
|
if(server->clients[i].server.cleanup) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "Client %d cleanup\n", server->clients[i].fd);
|
|
#endif
|
|
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
|
|
close(server->clients[i].fd);
|
|
arrdel(server->clients, i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
free(pfd);
|
|
}
|
|
}
|
|
|
|
void fishsoup_server_on_client(fishsoup_server_t* server, void (*on_client)(fishsoup_server_t* server, fishsoup_client_t* client)) {
|
|
server->on_client = on_client;
|
|
}
|
|
|
|
void fishsoup_server_on_tick(fishsoup_server_t* server, void (*on_tick)(fishsoup_server_t* server)) {
|
|
server->on_tick = on_tick;
|
|
}
|
|
|
|
void fishsoup_server_destroy(fishsoup_server_t* server) {
|
|
int i;
|
|
for(i = 0; i < arrlen(server->clients); i++) {
|
|
if(server->clients[i].on_disconnect != NULL) server->clients[i].on_disconnect(&server->clients[i]);
|
|
close(server->clients[i].fd);
|
|
}
|
|
arrfree(server->clients);
|
|
close(server->fd);
|
|
free(server);
|
|
}
|
|
|
|
void fishsoup_server_broadcast(fishsoup_server_t* server, fishsoup_packet_t* packet) {
|
|
int i;
|
|
for(i = 0; i < arrlen(server->clients); i++) {
|
|
if(fishsoup_packet_send(server->clients[i].fd, packet) == 0) {
|
|
server->clients[i].server.cleanup = 1;
|
|
}
|
|
}
|
|
}
|