network/core/tcp.c
branchcustombridgeheads
changeset 5642 bfa6074e2833
equal deleted inserted replaced
5641:d4d00a16ef26 5642:bfa6074e2833
       
     1 /* $Id$ */
       
     2 
       
     3 #ifdef ENABLE_NETWORK
       
     4 
       
     5 #include "../../stdafx.h"
       
     6 #include "../../debug.h"
       
     7 #include "../../openttd.h"
       
     8 #include "../../variables.h"
       
     9 #include "../../table/strings.h"
       
    10 #include "../../functions.h"
       
    11 
       
    12 #include "os_abstraction.h"
       
    13 #include "config.h"
       
    14 #include "packet.h"
       
    15 #include "../network_data.h"
       
    16 #include "tcp.h"
       
    17 
       
    18 /**
       
    19  * @file tcp.c Basic functions to receive and send TCP packets.
       
    20  */
       
    21 
       
    22 /**
       
    23  * Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit
       
    24  *  A socket can make errors. When that happens this handles what to do.
       
    25  * For clients: close connection and drop back to main-menu
       
    26  * For servers: close connection and that is it
       
    27  * @param cs the client to close the connection of
       
    28  * @return the new status
       
    29  */
       
    30 NetworkRecvStatus CloseConnection(NetworkClientState *cs)
       
    31 {
       
    32 	NetworkCloseClient(cs);
       
    33 
       
    34 	/* Clients drop back to the main menu */
       
    35 	if (!_network_server && _networking) {
       
    36 		_switch_mode = SM_MENU;
       
    37 		_networking = false;
       
    38 		_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
       
    39 
       
    40 		return NETWORK_RECV_STATUS_CONN_LOST;
       
    41 	}
       
    42 
       
    43 	return NETWORK_RECV_STATUS_OKAY;
       
    44 }
       
    45 
       
    46 /**
       
    47  * Whether the client has quit or not (used in packet.c)
       
    48  * @param cs the client to check
       
    49  * @return true if the client has quit
       
    50  */
       
    51 bool HasClientQuit(NetworkClientState *cs)
       
    52 {
       
    53 	return cs->has_quit;
       
    54 }
       
    55 
       
    56 /**
       
    57  * This function puts the packet in the send-queue and it is send as
       
    58  * soon as possible. This is the next tick, or maybe one tick later
       
    59  * if the OS-network-buffer is full)
       
    60  * @param packet the packet to send
       
    61  * @param cs     the client to send to
       
    62  */
       
    63 void NetworkSend_Packet(Packet *packet, NetworkClientState *cs)
       
    64 {
       
    65 	Packet *p;
       
    66 	assert(packet != NULL);
       
    67 
       
    68 	packet->pos = 0;
       
    69 	packet->next = NULL;
       
    70 
       
    71 	NetworkSend_FillPacketSize(packet);
       
    72 
       
    73 	/* Locate last packet buffered for the client */
       
    74 	p = cs->packet_queue;
       
    75 	if (p == NULL) {
       
    76 		/* No packets yet */
       
    77 		cs->packet_queue = packet;
       
    78 	} else {
       
    79 		/* Skip to the last packet */
       
    80 		while (p->next != NULL) p = p->next;
       
    81 		p->next = packet;
       
    82 	}
       
    83 }
       
    84 
       
    85 /**
       
    86  * Sends all the buffered packets out for this client. It stops when:
       
    87  *   1) all packets are send (queue is empty)
       
    88  *   2) the OS reports back that it can not send any more
       
    89  *      data right now (full network-buffer, it happens ;))
       
    90  *   3) sending took too long
       
    91  * @param cs the client to send the packets for
       
    92  */
       
    93 bool NetworkSend_Packets(NetworkClientState *cs)
       
    94 {
       
    95 	ssize_t res;
       
    96 	Packet *p;
       
    97 
       
    98 	/* We can not write to this socket!! */
       
    99 	if (!cs->writable) return false;
       
   100 	if (cs->socket == INVALID_SOCKET) return false;
       
   101 
       
   102 	p = cs->packet_queue;
       
   103 	while (p != NULL) {
       
   104 		res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
       
   105 		if (res == -1) {
       
   106 			int err = GET_LAST_ERROR();
       
   107 			if (err != EWOULDBLOCK) {
       
   108 				/* Something went wrong.. close client! */
       
   109 				DEBUG(net, 0, "send failed with error %d", err);
       
   110 				CloseConnection(cs);
       
   111 				return false;
       
   112 			}
       
   113 			return true;
       
   114 		}
       
   115 		if (res == 0) {
       
   116 			/* Client/server has left us :( */
       
   117 			CloseConnection(cs);
       
   118 			return false;
       
   119 		}
       
   120 
       
   121 		p->pos += res;
       
   122 
       
   123 		/* Is this packet sent? */
       
   124 		if (p->pos == p->size) {
       
   125 			/* Go to the next packet */
       
   126 			cs->packet_queue = p->next;
       
   127 			free(p);
       
   128 			p = cs->packet_queue;
       
   129 		} else {
       
   130 			return true;
       
   131 		}
       
   132 	}
       
   133 
       
   134 	return true;
       
   135 }
       
   136 
       
   137 /**
       
   138  * Receives a packet for the given client
       
   139  * @param cs     the client to (try to) receive a packet for
       
   140  * @param status the variable to store the status into
       
   141  * @return the received packet (or NULL when it didn't receive one)
       
   142  */
       
   143 Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status)
       
   144 {
       
   145 	ssize_t res;
       
   146 	Packet *p;
       
   147 
       
   148 	*status = NETWORK_RECV_STATUS_OKAY;
       
   149 
       
   150 	if (cs->socket == INVALID_SOCKET) return NULL;
       
   151 
       
   152 	if (cs->packet_recv == NULL) {
       
   153 		cs->packet_recv = malloc(sizeof(Packet));
       
   154 		if (cs->packet_recv == NULL) error("Failed to allocate packet");
       
   155 		/* Set pos to zero! */
       
   156 		cs->packet_recv->pos = 0;
       
   157 		cs->packet_recv->size = 0; // Can be ommited, just for safety reasons
       
   158 	}
       
   159 
       
   160 	p = cs->packet_recv;
       
   161 
       
   162 	/* Read packet size */
       
   163 	if (p->pos < sizeof(PacketSize)) {
       
   164 		while (p->pos < sizeof(PacketSize)) {
       
   165 			/* Read the size of the packet */
       
   166 			res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
       
   167 			if (res == -1) {
       
   168 				int err = GET_LAST_ERROR();
       
   169 				if (err != EWOULDBLOCK) {
       
   170 					/* Something went wrong... (104 is connection reset by peer) */
       
   171 					if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
       
   172 					*status = CloseConnection(cs);
       
   173 					return NULL;
       
   174 				}
       
   175 				/* Connection would block, so stop for now */
       
   176 				return NULL;
       
   177 			}
       
   178 			if (res == 0) {
       
   179 				/* Client/server has left */
       
   180 				*status = CloseConnection(cs);
       
   181 				return NULL;
       
   182 			}
       
   183 			p->pos += res;
       
   184 		}
       
   185 
       
   186 		NetworkRecv_ReadPacketSize(p);
       
   187 
       
   188 		if (p->size > SEND_MTU) {
       
   189 			*status = CloseConnection(cs);
       
   190 			return NULL;
       
   191 		}
       
   192 	}
       
   193 
       
   194 	/* Read rest of packet */
       
   195 	while (p->pos < p->size) {
       
   196 		res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
       
   197 		if (res == -1) {
       
   198 			int err = GET_LAST_ERROR();
       
   199 			if (err != EWOULDBLOCK) {
       
   200 				/* Something went wrong... (104 is connection reset by peer) */
       
   201 				if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
       
   202 				*status = CloseConnection(cs);
       
   203 				return NULL;
       
   204 			}
       
   205 			/* Connection would block */
       
   206 			return NULL;
       
   207 		}
       
   208 		if (res == 0) {
       
   209 			/* Client/server has left */
       
   210 			*status = CloseConnection(cs);
       
   211 			return NULL;
       
   212 		}
       
   213 
       
   214 		p->pos += res;
       
   215 	}
       
   216 
       
   217 	/* We have a complete packet, return it! */
       
   218 	p->pos = 2;
       
   219 	p->next = NULL; // Should not be needed, but who knows...
       
   220 
       
   221 	/* Prepare for receiving a new packet */
       
   222 	cs->packet_recv = NULL;
       
   223 
       
   224 	return p;
       
   225 }
       
   226 
       
   227 #endif /* ENABLE_NETWORK */