celestar@5642: /* $Id$ */ celestar@5642: celestar@5642: #ifdef ENABLE_NETWORK celestar@5642: celestar@5642: #include "../../stdafx.h" celestar@5642: #include "../../debug.h" celestar@5642: #include "../../openttd.h" celestar@5642: #include "../../variables.h" rubidium@5643: #include "table/strings.h" celestar@5642: #include "../../functions.h" celestar@5642: celestar@5648: #include "../network_data.h" celestar@5642: #include "packet.h" celestar@5642: #include "tcp.h" celestar@5650: #include "../../helpers.hpp" celestar@5642: celestar@5642: /** celestar@5642: * @file tcp.c Basic functions to receive and send TCP packets. celestar@5642: */ celestar@5642: celestar@5642: /** celestar@5642: * Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit celestar@5642: * A socket can make errors. When that happens this handles what to do. celestar@5642: * For clients: close connection and drop back to main-menu celestar@5642: * For servers: close connection and that is it celestar@5642: * @param cs the client to close the connection of celestar@5642: * @return the new status celestar@5642: */ celestar@5642: NetworkRecvStatus CloseConnection(NetworkClientState *cs) celestar@5642: { celestar@5642: NetworkCloseClient(cs); celestar@5642: celestar@5642: /* Clients drop back to the main menu */ celestar@5642: if (!_network_server && _networking) { celestar@5642: _switch_mode = SM_MENU; celestar@5642: _networking = false; celestar@5642: _switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION; celestar@5642: celestar@5642: return NETWORK_RECV_STATUS_CONN_LOST; celestar@5642: } celestar@5642: celestar@5642: return NETWORK_RECV_STATUS_OKAY; celestar@5642: } celestar@5642: celestar@5642: /** celestar@5642: * Whether the client has quit or not (used in packet.c) celestar@5642: * @param cs the client to check celestar@5642: * @return true if the client has quit celestar@5642: */ celestar@5648: bool HasClientQuit(const NetworkClientState *cs) celestar@5642: { celestar@5642: return cs->has_quit; celestar@5642: } celestar@5642: celestar@5642: /** celestar@5642: * This function puts the packet in the send-queue and it is send as celestar@5642: * soon as possible. This is the next tick, or maybe one tick later celestar@5642: * if the OS-network-buffer is full) celestar@5642: * @param packet the packet to send celestar@5642: * @param cs the client to send to celestar@5642: */ celestar@5642: void NetworkSend_Packet(Packet *packet, NetworkClientState *cs) celestar@5642: { celestar@5642: Packet *p; celestar@5642: assert(packet != NULL); celestar@5642: celestar@5642: packet->pos = 0; celestar@5642: packet->next = NULL; celestar@5642: celestar@5642: NetworkSend_FillPacketSize(packet); celestar@5642: celestar@5642: /* Locate last packet buffered for the client */ celestar@5642: p = cs->packet_queue; celestar@5642: if (p == NULL) { celestar@5642: /* No packets yet */ celestar@5642: cs->packet_queue = packet; celestar@5642: } else { celestar@5642: /* Skip to the last packet */ celestar@5642: while (p->next != NULL) p = p->next; celestar@5642: p->next = packet; celestar@5642: } celestar@5642: } celestar@5642: celestar@5642: /** celestar@5642: * Sends all the buffered packets out for this client. It stops when: celestar@5642: * 1) all packets are send (queue is empty) celestar@5642: * 2) the OS reports back that it can not send any more celestar@5642: * data right now (full network-buffer, it happens ;)) celestar@5642: * 3) sending took too long celestar@5642: * @param cs the client to send the packets for celestar@5642: */ celestar@5642: bool NetworkSend_Packets(NetworkClientState *cs) celestar@5642: { celestar@5642: ssize_t res; celestar@5642: Packet *p; celestar@5642: celestar@5642: /* We can not write to this socket!! */ celestar@5642: if (!cs->writable) return false; celestar@5642: if (cs->socket == INVALID_SOCKET) return false; celestar@5642: celestar@5642: p = cs->packet_queue; celestar@5642: while (p != NULL) { celestar@5650: res = send(cs->socket, (const char*)p->buffer + p->pos, p->size - p->pos, 0); celestar@5642: if (res == -1) { celestar@5642: int err = GET_LAST_ERROR(); celestar@5642: if (err != EWOULDBLOCK) { celestar@5642: /* Something went wrong.. close client! */ celestar@5642: DEBUG(net, 0, "send failed with error %d", err); celestar@5642: CloseConnection(cs); celestar@5642: return false; celestar@5642: } celestar@5642: return true; celestar@5642: } celestar@5642: if (res == 0) { celestar@5642: /* Client/server has left us :( */ celestar@5642: CloseConnection(cs); celestar@5642: return false; celestar@5642: } celestar@5642: celestar@5642: p->pos += res; celestar@5642: celestar@5642: /* Is this packet sent? */ celestar@5642: if (p->pos == p->size) { celestar@5642: /* Go to the next packet */ celestar@5642: cs->packet_queue = p->next; celestar@5642: free(p); celestar@5642: p = cs->packet_queue; celestar@5642: } else { celestar@5642: return true; celestar@5642: } celestar@5642: } celestar@5642: celestar@5642: return true; celestar@5642: } celestar@5642: celestar@5642: /** celestar@5642: * Receives a packet for the given client celestar@5642: * @param cs the client to (try to) receive a packet for celestar@5642: * @param status the variable to store the status into celestar@5642: * @return the received packet (or NULL when it didn't receive one) celestar@5642: */ celestar@5642: Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status) celestar@5642: { celestar@5642: ssize_t res; celestar@5642: Packet *p; celestar@5642: celestar@5642: *status = NETWORK_RECV_STATUS_OKAY; celestar@5642: celestar@5642: if (cs->socket == INVALID_SOCKET) return NULL; celestar@5642: celestar@5642: if (cs->packet_recv == NULL) { celestar@5650: MallocT(&cs->packet_recv, 1); celestar@5642: if (cs->packet_recv == NULL) error("Failed to allocate packet"); celestar@5642: /* Set pos to zero! */ celestar@5642: cs->packet_recv->pos = 0; celestar@5642: cs->packet_recv->size = 0; // Can be ommited, just for safety reasons celestar@5642: } celestar@5642: celestar@5642: p = cs->packet_recv; celestar@5642: celestar@5642: /* Read packet size */ celestar@5642: if (p->pos < sizeof(PacketSize)) { celestar@5642: while (p->pos < sizeof(PacketSize)) { celestar@5642: /* Read the size of the packet */ celestar@5650: res = recv(cs->socket, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0); celestar@5642: if (res == -1) { celestar@5642: int err = GET_LAST_ERROR(); celestar@5642: if (err != EWOULDBLOCK) { celestar@5642: /* Something went wrong... (104 is connection reset by peer) */ celestar@5642: if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); celestar@5642: *status = CloseConnection(cs); celestar@5642: return NULL; celestar@5642: } celestar@5642: /* Connection would block, so stop for now */ celestar@5642: return NULL; celestar@5642: } celestar@5642: if (res == 0) { celestar@5642: /* Client/server has left */ celestar@5642: *status = CloseConnection(cs); celestar@5642: return NULL; celestar@5642: } celestar@5642: p->pos += res; celestar@5642: } celestar@5642: celestar@5642: NetworkRecv_ReadPacketSize(p); celestar@5642: celestar@5642: if (p->size > SEND_MTU) { celestar@5642: *status = CloseConnection(cs); celestar@5642: return NULL; celestar@5642: } celestar@5642: } celestar@5642: celestar@5642: /* Read rest of packet */ celestar@5642: while (p->pos < p->size) { celestar@5650: res = recv(cs->socket, (char*)p->buffer + p->pos, p->size - p->pos, 0); celestar@5642: if (res == -1) { celestar@5642: int err = GET_LAST_ERROR(); celestar@5642: if (err != EWOULDBLOCK) { celestar@5642: /* Something went wrong... (104 is connection reset by peer) */ celestar@5642: if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); celestar@5642: *status = CloseConnection(cs); celestar@5642: return NULL; celestar@5642: } celestar@5642: /* Connection would block */ celestar@5642: return NULL; celestar@5642: } celestar@5642: if (res == 0) { celestar@5642: /* Client/server has left */ celestar@5642: *status = CloseConnection(cs); celestar@5642: return NULL; celestar@5642: } celestar@5642: celestar@5642: p->pos += res; celestar@5642: } celestar@5642: celestar@5642: /* We have a complete packet, return it! */ celestar@5642: p->pos = 2; celestar@5642: p->next = NULL; // Should not be needed, but who knows... celestar@5642: celestar@5642: /* Prepare for receiving a new packet */ celestar@5642: cs->packet_recv = NULL; celestar@5642: celestar@5642: return p; celestar@5642: } celestar@5642: celestar@5642: #endif /* ENABLE_NETWORK */