Program Listing for File client_io.c¶
↰ Return to documentation for file (server/client/client_io.c)
#include <poll.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "server.h"
#include "client.h"
#include "utils/resizable_array.h"
static constexpr const size_t BUFFER_SIZE = 1024;
static constexpr const size_t ITER_MAX = 4;
struct network_data_s {
server_t *srv;
client_state_t *client;
};
static
void error_helper(server_t *srv, const char *msg, uint32_t idx)
{
perror(msg);
remove_client(srv, idx);
}
static
bool recv_wrapper(server_t *srv, uint32_t idx, char *buffer, ssize_t *res)
{
client_state_t *client = srv->cm.clients + idx;
ssize_t recv_res = recv(client->fd, buffer, BUFFER_SIZE - 1, 0);
if (recv_res < 0) {
DEBUG("fd = %d, idx = %u", client->fd, idx);
error_helper(srv, "recv failed", idx);
return false;
}
if (recv_res == 0) {
remove_client(srv, idx);
return false;
}
*res = recv_res;
return true;
}
void read_client(server_t *srv, uint32_t idx)
{
char buffer[BUFFER_SIZE] = {0};
ssize_t recv_res = sizeof(buffer) - 1;
client_state_t *client = srv->cm.clients + idx;
if (client == nullptr)
return;
for (size_t i = 0; i < ITER_MAX && recv_res == sizeof(buffer) - 1; i++) {
if (!recv_wrapper(srv, idx, buffer, &recv_res))
return;
if (!sized_struct_ensure_capacity(
&client->input, recv_res + 1, sizeof *client->input.buff)) {
error_helper(srv, "Input buffer resize failed", idx);
return;
}
memcpy(client->input.buff + client->input.nmemb, buffer, recv_res);
client->input.nmemb += recv_res;
client->input.buff[client->input.nmemb] = '\0';
}
DEBUG("Received from client %d: %s", client->fd, buffer);
}
void write_client(server_t *srv, uint32_t idx)
{
client_state_t *cl = srv->cm.clients + idx;
ssize_t sent;
size_t line_len;
if (!cl || cl->output.nmemb <= cl->out_buff_idx)
return;
line_len = strcspn(cl->output.buff + cl->out_buff_idx, "\n");
if ((cl->output.buff + cl->out_buff_idx)[line_len] != '\n')
return;
sent = send(cl->fd, cl->output.buff + cl->out_buff_idx, line_len + 1, 0);
if (sent < 0) {
error_helper(srv, "send failed", idx - 1);
return;
}
cl->out_buff_idx += sent;
if (cl->out_buff_idx != cl->output.nmemb)
return;
cl->output.nmemb = 0;
cl->out_buff_idx = 0;
srv->cm.server_pfds[idx].events &= ~POLLOUT;
}
void append_to_output(server_t *srv, client_state_t *client, const char *msg)
{
size_t len = strlen(msg);
size_t idx = client - srv->cm.clients;
if (!sized_struct_ensure_capacity(&client->output, len + 1,
sizeof *client->output.buff)) {
error_helper(srv, "Output buffer resize failed", idx + 1);
return;
}
strncpy(client->output.buff + client->output.nmemb, msg, len + 1);
client->output.nmemb += len;
if (!strchr(msg, '\n'))
return;
srv->cm.server_pfds[idx].events |= POLLOUT;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
static int compute_formatted_size(const char *fmt, va_list args)
{
va_list args_copy;
int len;
va_copy(args_copy, args);
len = vsnprintf(nullptr, 0, fmt, args_copy);
va_end(args_copy);
return len;
}
static void fill_and_append(
struct network_data_s *data, size_t size, const char *fmt, va_list args)
{
char buffer[size + 1];
vsnprintf(buffer, size + 1, fmt, args);
buffer[size] = '\0';
append_to_output(data->srv, data->client, buffer);
}
void vappend_to_output(server_t *srv,
client_state_t *client, const char *fmt, ...)
{
va_list args;
int size;
struct network_data_s data = {srv, client};
va_start(args, fmt);
size = compute_formatted_size(fmt, args);
if (size < 0) {
perror("vsnprintf failed to compute size");
va_end(args);
return;
}
fill_and_append(&data, (size_t)size, fmt, args);
va_end(args);
}
// TODO: Improve this function by splitting GUI team into its own v-array
void send_to_guis(server_t *srv, const char *fmt, ...)
{
va_list args;
int size;
static char buff[BUFFER_SIZE] = {};
va_start(args, fmt);
size = compute_formatted_size(fmt, args);
vsnprintf(buff, size + 1, fmt, args);
DEBUG("send to guis: [%s]", buff);
for (size_t i = srv->cm.idx_of_gui; i < srv->cm.idx_of_players; i++)
append_to_output(srv, &srv->cm.clients[i], buff);
}
#pragma clang diagnostic pop