/************************************************************************** presult indicates if there is more packets in the cache. We return result instead of just testing if the returning package is NULL as we sometimes return a NULL packet even if everything is OK (receive_packet_goto_route). **************************************************************************/ void *get_packet_from_connection(struct connection *pc, enum packet_type *ptype, bool * presult) { int len_read; int whole_packet_len; struct { enum packet_type type; int itype; } utype; struct data_in din; #ifdef USE_COMPRESSION bool compressed_packet = FALSE; int header_size = 0; #endif *presult = FALSE; if (!pc->used) { return NULL; /* connection was closed, stop reading */ } if (pc->buffer->ndata < 3) { return NULL; /* length and type not read */ } dio_input_init(&din, pc->buffer->data, pc->buffer->ndata); dio_get_uint16(&din, &len_read); /* The non-compressed case */ whole_packet_len = len_read; #ifdef USE_COMPRESSION if (len_read == JUMBO_SIZE) { compressed_packet = TRUE; header_size = 6; if (dio_input_remaining(&din) >= 4) { dio_get_uint32(&din, &whole_packet_len); log_compress("COMPRESS: got a jumbo packet of size %d", whole_packet_len); } else { /* to return NULL below */ whole_packet_len = 6; } } else if (len_read >= COMPRESSION_BORDER) { compressed_packet = TRUE; header_size = 2; whole_packet_len = len_read - COMPRESSION_BORDER; log_compress("COMPRESS: got a normal packet of size %d", whole_packet_len); } #endif if ((unsigned)whole_packet_len > pc->buffer->ndata) { return NULL; /* not all data has been read */ } #ifdef USE_COMPRESSION if (compressed_packet) { uLong compressed_size = whole_packet_len - header_size; /* * We don't know the decompressed size. We assume a bad case * here: an expansion by an factor of 100. */ unsigned long int decompressed_size = 100 * compressed_size; void *decompressed = fc_malloc(decompressed_size); int error; struct socket_packet_buffer *buffer = pc->buffer; error = uncompress(decompressed, &decompressed_size, ADD_TO_POINTER(buffer->data, header_size), compressed_size); if (error != Z_OK) { log_verbose("Uncompressing of the packet stream failed. " "The connection will be closed now."); connection_close(pc, _("decoding error")); return NULL; } buffer->ndata -= whole_packet_len; /* * Remove the packet with the compressed data and shift all the * remaining data to the front. */ memmove(buffer->data, buffer->data + whole_packet_len, buffer->ndata); if (buffer->ndata + decompressed_size > buffer->nsize) { buffer->nsize += decompressed_size; buffer->data = fc_realloc(buffer->data, buffer->nsize); } /* * Make place for the uncompressed data by moving the remaining * data. */ memmove(buffer->data + decompressed_size, buffer->data, buffer->ndata); /* * Copy the uncompressed data. */ memcpy(buffer->data, decompressed, decompressed_size); free(decompressed); buffer->ndata += decompressed_size; log_compress("COMPRESS: decompressed %ld into %ld", compressed_size, decompressed_size); return get_packet_from_connection(pc, ptype, presult); } #endif /* * At this point the packet is a plain uncompressed one. These have * to have to be at least 3 bytes in size. */ if (whole_packet_len < 3) { log_verbose("The packet stream is corrupt. The connection " "will be closed now."); connection_close(pc, _("decoding error")); return NULL; } dio_get_uint8(&din, &utype.itype); utype.type = utype.itype; log_packet("got packet type=(%s)%d len=%d from %s", packet_name(utype.type), utype.itype, whole_packet_len, is_server() ? pc->username : "******"); *ptype = utype.type; *presult = TRUE; if (pc->incoming_packet_notify) { pc->incoming_packet_notify(pc, utype.type, whole_packet_len); } #if PACKET_SIZE_STATISTICS { static struct { int counter; int size; } packets_stats[PACKET_LAST]; static int packet_counter = 0; int packet_type = utype.itype; int size = whole_packet_len; if (!packet_counter) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } } packets_stats[packet_type].counter++; packets_stats[packet_type].size += size; packet_counter++; if (packet_counter % 100 == 0) { int i, sum = 0; log_test("Received packets:"); for (i = 0; i < PACKET_LAST; i++) { if (packets_stats[i].counter == 0) continue; sum += packets_stats[i].size; log_test(" [%-25.25s %3d]: %6d packets; %8d bytes total; " "%5d bytes/packet average", packet_name(i), i, packets_stats[i].counter, packets_stats[i].size, packets_stats[i].size / packets_stats[i].counter); } log_test("received %d bytes in %d packets;average size " "per packet %d bytes", sum, packet_counter, sum / packet_counter); } } #endif return get_packet_from_connection_helper(pc, utype.type); }
/************************************************************************** It returns the request id of the outgoing packet (or 0 if is_server()). **************************************************************************/ int send_packet_data(struct connection *pc, unsigned char *data, int len, enum packet_type packet_type) { /* default for the server */ int result = 0; log_packet("sending packet type=%s(%d) len=%d to %s", packet_name(packet_type), packet_type, len, is_server() ? pc->username : "******"); if (!is_server()) { pc->client.last_request_id_used = get_next_request_id(pc->client.last_request_id_used); result = pc->client.last_request_id_used; log_packet("sending request %d", result); } if (pc->outgoing_packet_notify) { pc->outgoing_packet_notify(pc, packet_type, len, result); } #ifdef USE_COMPRESSION if (TRUE) { int size = len; if (conn_compression_frozen(pc)) { size_t old_size; /* Keep this a decent amount less than MAX_LEN_BUFFER to avoid the * (remote) possibility of trying to dump MAX_LEN_BUFFER to the * network in one go */ #define MAX_LEN_COMPRESS_QUEUE (MAX_LEN_BUFFER/2) FC_STATIC_ASSERT(MAX_LEN_COMPRESS_QUEUE < MAX_LEN_BUFFER, compress_queue_maxlen_too_big); /* If this packet would cause us to overfill the queue, flush * everything that's in there already before queuing this one */ if (MAX_LEN_COMPRESS_QUEUE < byte_vector_size(&pc->compression.queue) + len) { log_compress2("COMPRESS: huge queue, forcing to flush (%lu/%lu)", (long unsigned) byte_vector_size(&pc->compression.queue), (long unsigned) MAX_LEN_COMPRESS_QUEUE); if (!conn_compression_flush(pc)) { return -1; } byte_vector_reserve(&pc->compression.queue, 0); } old_size = byte_vector_size(&pc->compression.queue); byte_vector_reserve(&pc->compression.queue, old_size + len); memcpy(pc->compression.queue.p + old_size, data, len); log_compress2("COMPRESS: putting %s into the queue", packet_name(packet_type)); } else { stat_size_alone += size; log_compress("COMPRESS: sending %s alone (%d bytes total)", packet_name(packet_type), stat_size_alone); connection_send_data(pc, data, len); } log_compress2("COMPRESS: STATS: alone=%d compression-expand=%d " "compression (before/after) = %d/%d", stat_size_alone, stat_size_no_compression, stat_size_uncompressed, stat_size_compressed); } #else /* USE_COMPRESSION */ connection_send_data(pc, data, len); #endif /* USE_COMPRESSION */ #if PACKET_SIZE_STATISTICS { static struct { int counter; int size; } packets_stats[PACKET_LAST]; static int packet_counter = 0; static int last_start_turn_seen = -1; static bool start_turn_seen = FALSE; int size = len; bool print = FALSE; bool clear = FALSE; if (!packet_counter) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } } packets_stats[packet_type].counter++; packets_stats[packet_type].size += size; packet_counter++; if (packet_type == PACKET_START_TURN && last_start_turn_seen != game.turn) { start_turn_seen=TRUE; last_start_turn_seen = game.turn; } if ((packet_type == PACKET_PROCESSING_FINISHED || packet_type == PACKET_THAW_HINT) && start_turn_seen) { start_turn_seen = FALSE; print = TRUE; clear = TRUE; } if(print) { int i, sum = 0; #define log_ll log_debug #if PACKET_SIZE_STATISTICS == 2 delta_stats_report(); #endif log_ll("Transmitted packets:"); log_ll("%8s %8s %8s %s", "Packets", "Bytes", "Byt/Pac", "Name"); for (i = 0; i < PACKET_LAST; i++) { if (packets_stats[i].counter == 0) { continue; } sum += packets_stats[i].size; log_ll("%8d %8d %8d %s(%i)", packets_stats[i].counter, packets_stats[i].size, packets_stats[i].size / packets_stats[i].counter, packet_name(i),i); } log_test("turn=%d; transmitted %d bytes in %d packets;average size " "per packet %d bytes", game.turn, sum, packet_counter, sum / packet_counter); log_test("turn=%d; transmitted %d bytes", game.turn, pc->statistics.bytes_send); } if (clear) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } packet_counter = 0; pc->statistics.bytes_send = 0; delta_stats_reset(); } } #undef log_ll #endif /* PACKET_SIZE_STATISTICS */ return result; }
/************************************************************************** It returns the request id of the outgoing packet (or 0 if is_server()). **************************************************************************/ int send_packet_data(struct connection *pc, unsigned char *data, int len) { /* default for the server */ int result = 0; log_packet("sending packet type=%s(%d) len=%d to %s", packet_name(data[2]), data[2], len, is_server() ? pc->username : "******"); if (!is_server()) { pc->client.last_request_id_used = get_next_request_id(pc->client.last_request_id_used); result = pc->client.last_request_id_used; log_packet("sending request %d", result); } if (pc->outgoing_packet_notify) { pc->outgoing_packet_notify(pc, data[2], len, result); } #ifdef USE_COMPRESSION if (TRUE) { int packet_type; int size = len; packet_type = data[2]; if (conn_compression_frozen(pc)) { size_t old_size = pc->compression.queue.size; byte_vector_reserve(&pc->compression.queue, old_size + len); memcpy(pc->compression.queue.p + old_size, data, len); log_compress2("COMPRESS: putting %s into the queue", packet_name(packet_type)); if (MAX_LEN_BUFFER < byte_vector_size(&pc->compression.queue)) { log_compress2("COMPRESS: huge queue, forcing to flush (%lu/%lu)", (long unsigned) byte_vector_size(&pc->compression.queue), (long unsigned) MAX_LEN_BUFFER); if (!conn_compression_flush(pc)) { return -1; } byte_vector_reserve(&pc->compression.queue, 0); } } else { stat_size_alone += size; log_compress("COMPRESS: sending %s alone (%d bytes total)", packet_name(packet_type), stat_size_alone); connection_send_data(pc, data, len); } log_compress2("COMPRESS: STATS: alone=%d compression-expand=%d " "compression (before/after) = %d/%d", stat_size_alone, stat_size_no_compression, stat_size_uncompressed, stat_size_compressed); } #else connection_send_data(pc, data, len); #endif #if PACKET_SIZE_STATISTICS { static struct { int counter; int size; } packets_stats[PACKET_LAST]; static int packet_counter = 0; static int last_start_turn_seen = -1; static bool start_turn_seen = FALSE; int packet_type = data[2]; int size = len; bool print = FALSE; bool clear = FALSE; if (!packet_counter) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } } packets_stats[packet_type].counter++; packets_stats[packet_type].size += size; packet_counter++; if (packet_type == PACKET_START_TURN && last_start_turn_seen != game.turn) { start_turn_seen=TRUE; last_start_turn_seen = game.turn; } if ((packet_type == PACKET_PROCESSING_FINISHED || packet_type == PACKET_THAW_HINT) && start_turn_seen) { start_turn_seen = FALSE; print = TRUE; clear = TRUE; } if(print) { int i, sum = 0; #define log_ll log_debug #if PACKET_SIZE_STATISTICS == 2 delta_stats_report(); #endif log_ll("Transmitted packets:"); log_ll("%8s %8s %8s %s", "Packets", "Bytes", "Byt/Pac", "Name"); for (i = 0; i < PACKET_LAST; i++) { if (packets_stats[i].counter == 0) { continue; } sum += packets_stats[i].size; log_ll("%8d %8d %8d %s(%i)", packets_stats[i].counter, packets_stats[i].size, packets_stats[i].size / packets_stats[i].counter, packet_name(i),i); } log_test("turn=%d; transmitted %d bytes in %d packets;average size " "per packet %d bytes", game.turn, sum, packet_counter, sum / packet_counter); log_test("turn=%d; transmitted %d bytes", game.turn, pc->statistics.bytes_send); } if (clear) { int i; for (i = 0; i < PACKET_LAST; i++) { packets_stats[i].counter = 0; packets_stats[i].size = 0; } packet_counter = 0; pc->statistics.bytes_send = 0; delta_stats_reset(); } } #undef log_ll #endif /* PACKET_SIZE_STATISTICS */ return result; }
int server(unsigned short port, int demarcaciones, int taxPorDemarcacion, int iters, int timeout_millis, int txdelay_millis, bool debug) { connection_t conn; packet_t outgoing; packet_t * incoming; if (!connection_server(&conn, port)) { fprintf(stderr, "Unable to spawn server on port %u: ", port); connection_error(&conn, stderr); fprintf(stderr, "\n"); return 1; } if (debug) { connection_debug(&conn, stderr); } gestion_taxi_t * taxis = calloc(demarcaciones * taxPorDemarcacion, sizeof(gestion_taxi_t)); if (taxis == NULL) { fprintf(stderr, "Failed to allocate memory for taxi stats structure: %s\n", strerror(errno)); return 1; } fprintf(stderr, "Waiting for clientes at port: [%d]\n", port); int connected = 0; int pendingAccAck = demarcaciones * taxPorDemarcacion; while (pendingAccAck > 0) { if (!connection_receive(&conn, &incoming, NULL)) { /* TODO: check how to safely implement a timeout here */ fprintf(stderr, "Error receiving login packet: "); connection_error(&conn, stderr); fprintf(stderr, "\n"); if (connection_dead(&conn)) { connection_close(&conn); free(taxis); return 1; } continue; } bool duplicate = false; int pos; switch (incoming->type) { case PACKET_TYPE_LOGIN: if (incoming->login.version != PACKET_VERSION) { fprintf(stderr, "Got LOGIN with version %08X but server version is %08X\n", incoming->login.version, PACKET_VERSION); connection_discard(&conn); continue; } connection_accept(&conn); for (pos = 0; pos < connected; pos++) { if (connection_cmp(&taxis[pos].conexionConTaxi, &conn)) { duplicate = true; fprintf(stderr, "Got duplicated login packet from taxi %d-%d. Re-sending acknowledgement\n", pos / taxPorDemarcacion, pos % taxPorDemarcacion); break; } } if (!duplicate) { if (connected == taxPorDemarcacion * demarcaciones) { fprintf(stderr, "Received new LOGIN but all taxis have been already registered\n"); continue; } fprintf(stderr, "Registered new taxi %d-%d\n", connected / taxPorDemarcacion, connected % taxPorDemarcacion); memcpy(&taxis[connected].conexionConTaxi, &conn, sizeof(conn)); connected++; } outgoing.type = PACKET_TYPE_ACCEPT; outgoing.accept.zone = pos / taxPorDemarcacion; outgoing.accept.id = pos % taxPorDemarcacion; outgoing.accept.neighbors = taxPorDemarcacion - 1; outgoing.accept.ticks = iters; outgoing.accept.timeout = timeout_millis; outgoing.accept.txdelay = txdelay_millis; if (!connection_send(&conn, &outgoing)) { fprintf(stderr, "Error replying to login packet: "); connection_error(&conn, stderr); fprintf(stderr, "\n"); connection_close(&conn); free(taxis); return 1; } break; case PACKET_TYPE_ACCACK: if (incoming->accack.zone >= demarcaciones || incoming->accack.id >= taxPorDemarcacion) { fprintf(stderr, "Received illegal accept acknowledgement from taxi %u-%u\n", incoming->accack.zone, incoming->accack.id); connection_discard(&conn); continue; } connection_accept(&conn); gestion_taxi_t * taxi = &taxis[incoming->accack.zone * taxPorDemarcacion + incoming->accack.id]; if (!taxi->hasAckdAccept) { taxi->hasAckdAccept = true; pendingAccAck--; } break; default: connection_discard(&conn); fprintf(stderr, "Unexpected packet %s during login phase. Discarding.\n", packet_name(incoming)); continue; } } outgoing.type = PACKET_TYPE_START; for (int pos = 0; pos < demarcaciones * taxPorDemarcacion; pos++) { if (!connection_send(&taxis[pos].conexionConTaxi, &outgoing)) { fprintf(stderr, "Error sending start packet: "); connection_error(&conn, stderr); fprintf(stderr, "\n"); connection_close(&conn); free(taxis); return 1; } } int statsRcv = 0; struct timeval defaulttimeout = { (timeout_millis * 2) / 1000, ((timeout_millis * 2) % 1000) * 1000 }; struct timeval timeout = defaulttimeout; while (statsRcv < demarcaciones * taxPorDemarcacion) { if (!connection_receive(&conn, &incoming, NULL)) { fprintf(stderr, "Error receiving packet in main loop: "); connection_error(&conn, stderr); fprintf(stderr, "\n"); if (conn.err == CONNERR_TIMEOUT) { break; } if (connection_dead(&conn)) { connection_close(&conn); free(taxis); return 1; } continue; } gestion_taxi_t * taxi; switch (incoming->type) { case PACKET_TYPE_ACCACK: outgoing.type = PACKET_TYPE_START; if (!connection_send(&conn, &outgoing)) { fprintf(stderr, "Failed to send start packet in main loop: "); connection_error(&conn, stderr); fprintf(stderr, "\n"); connection_close(&conn); return 1; } timeout = defaulttimeout; break; case PACKET_TYPE_POSITION: if (incoming->position.zone > demarcaciones || incoming->position.id > taxPorDemarcacion) { fprintf(stderr, "Received position from illegal taxi %u-%u\n", outgoing.position.zone, outgoing.position.id); connection_discard(&conn); continue; } for (int neighbor = 0; neighbor < taxPorDemarcacion; neighbor++) { if (neighbor == incoming->position.id) { continue; } if (!connection_send(&taxis[incoming->position.zone * taxPorDemarcacion + neighbor].conexionConTaxi, incoming)) { fprintf(stderr, "Error forwarding position packet: "); connection_error(&taxis[incoming->position.zone * taxPorDemarcacion + neighbor].conexionConTaxi, stderr); fprintf(stderr, "\n"); connection_close(&conn); free(taxis); return 1; } } timeout = defaulttimeout; connection_accept(&conn); break; case PACKET_TYPE_POSACK: if (incoming->posack.zone > demarcaciones || incoming->posack.id > taxPorDemarcacion) { fprintf(stderr, "Received acknowledgement from illegal taxi %u-%u\n", incoming->posack.zone, incoming->posack.id); break; } if (incoming->posack.neighbor > taxPorDemarcacion) { fprintf(stderr, "Received acknowledgement from %u-%u targeting out-of-bounds taxi %u-%u\n", incoming->posack.zone, incoming->posack.id, incoming->posack.zone, incoming->posack.neighbor); break; } taxi = &taxis[incoming->posack.zone * taxPorDemarcacion + incoming->posack.neighbor]; if (!connection_send(&taxi->conexionConTaxi, incoming)) { fprintf(stderr, "Error forwarding acknowledgement packet: "); connection_error(&taxi->conexionConTaxi, stderr); fprintf(stderr, "\n"); connection_close(&conn); free(taxis); return 1; } timeout = defaulttimeout; connection_accept(&conn); break; case PACKET_TYPE_STATS: taxi = &taxis[incoming->stats.zone * taxPorDemarcacion + incoming->stats.id]; if (!taxi->hasSentStats) { printf("%u, %u, %u, %u, %u, %u, %u, %u\n", incoming->stats.zone, incoming->stats.id, incoming->stats.ticks, incoming->stats.posrcv, incoming->stats.ackrcv, incoming->stats.itmin, incoming->stats.itavg, incoming->stats.itmax ); taxi->hasSentStats = true; statsRcv++; } outgoing.type = PACKET_TYPE_STATACK; if (!connection_send(&taxi->conexionConTaxi, &outgoing)) { fprintf(stderr, "Error sending stats acknowledgement packet: "); connection_error(&taxi->conexionConTaxi, stderr); fprintf(stderr, "\n"); connection_close(&conn); free(taxis); return 1; } timeout = defaulttimeout; connection_accept(&conn); break; default: fprintf(stderr, "Unexpected packet %s during main loop\n", packet_name(incoming)); connection_discard(&conn); continue; } } // TODO: Interpretar estadísticas return 0; }