Maintain a circular buffer of recent commands, add to crashlog.
[openttd-joker.git] / src / network / core / tcp.cpp
blob2c2d0134499d5f8f9e26fd9e96ccd59d0ab682fa
1 /* $Id: tcp.cpp 26046 2013-11-22 21:41:19Z rubidium $ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /**
11 * @file tcp.cpp Basic functions to receive and send TCP packets.
14 #ifdef ENABLE_NETWORK
16 #include "../../stdafx.h"
17 #include "../../debug.h"
19 #include "tcp.h"
21 #include "../../safeguards.h"
23 /**
24 * Construct a socket handler for a TCP connection.
25 * @param s The just opened TCP connection.
27 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
28 NetworkSocketHandler(),
29 packet_queue(NULL), packet_recv(NULL),
30 sock(s), writable(false)
34 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
36 this->CloseConnection();
38 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
39 this->sock = INVALID_SOCKET;
42 NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
44 this->writable = false;
45 NetworkSocketHandler::CloseConnection(error);
47 /* Free all pending and partially received packets */
48 while (this->packet_queue != NULL) {
49 Packet *p = this->packet_queue->next;
50 delete this->packet_queue;
51 this->packet_queue = p;
53 delete this->packet_recv;
54 this->packet_recv = NULL;
56 return NETWORK_RECV_STATUS_OKAY;
59 /**
60 * This function puts the packet in the send-queue and it is send as
61 * soon as possible. This is the next tick, or maybe one tick later
62 * if the OS-network-buffer is full)
63 * @param packet the packet to send
65 void NetworkTCPSocketHandler::SendPacket(Packet *packet)
67 Packet *p;
68 assert(packet != NULL);
70 packet->PrepareToSend();
72 /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and
73 * keeping the other 1400+ bytes wastes memory, especially when someone tries
74 * to do a denial of service attack! */
75 packet->buffer = ReallocT(packet->buffer, packet->size);
77 /* Locate last packet buffered for the client */
78 p = this->packet_queue;
79 if (p == NULL) {
80 /* No packets yet */
81 this->packet_queue = packet;
82 } else {
83 /* Skip to the last packet */
84 while (p->next != NULL) p = p->next;
85 p->next = packet;
89 /**
90 * Sends all the buffered packets out for this client. It stops when:
91 * 1) all packets are send (queue is empty)
92 * 2) the OS reports back that it can not send any more
93 * data right now (full network-buffer, it happens ;))
94 * 3) sending took too long
95 * @param closing_down Whether we are closing down the connection.
96 * @return \c true if a (part of a) packet could be sent and
97 * the connection is not closed yet.
99 SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
101 ssize_t res;
102 Packet *p;
104 /* We can not write to this socket!! */
105 if (!this->writable) return SPS_NONE_SENT;
106 if (!this->IsConnected()) return SPS_CLOSED;
108 p = this->packet_queue;
109 while (p != NULL) {
110 res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0);
111 if (res == -1) {
112 int err = GET_LAST_ERROR();
113 if (err != EWOULDBLOCK) {
114 /* Something went wrong.. close client! */
115 if (!closing_down) {
116 DEBUG(net, 0, "send failed with error %d", err);
117 this->CloseConnection();
119 return SPS_CLOSED;
121 return SPS_PARTLY_SENT;
123 if (res == 0) {
124 /* Client/server has left us :( */
125 if (!closing_down) this->CloseConnection();
126 return SPS_CLOSED;
129 p->pos += res;
131 /* Is this packet sent? */
132 if (p->pos == p->size) {
133 /* Go to the next packet */
134 this->packet_queue = p->next;
135 delete p;
136 p = this->packet_queue;
137 } else {
138 return SPS_PARTLY_SENT;
142 return SPS_ALL_SENT;
146 * Receives a packet for the given client
147 * @return The received packet (or NULL when it didn't receive one)
149 Packet *NetworkTCPSocketHandler::ReceivePacket()
151 ssize_t res;
153 if (!this->IsConnected()) return NULL;
155 if (this->packet_recv == NULL) {
156 this->packet_recv = new Packet(this);
159 Packet *p = this->packet_recv;
161 /* Read packet size */
162 if (p->pos < sizeof(PacketSize)) {
163 while (p->pos < sizeof(PacketSize)) {
164 /* Read the size of the packet */
165 res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
166 if (res == -1) {
167 int err = GET_LAST_ERROR();
168 if (err != EWOULDBLOCK) {
169 /* Something went wrong... (104 is connection reset by peer) */
170 if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
171 this->CloseConnection();
172 return NULL;
174 /* Connection would block, so stop for now */
175 return NULL;
177 if (res == 0) {
178 /* Client/server has left */
179 this->CloseConnection();
180 return NULL;
182 p->pos += res;
185 /* Read the packet size from the received packet */
186 p->ReadRawPacketSize();
188 if (p->size > SEND_MTU) {
189 this->CloseConnection();
190 return NULL;
194 /* Read rest of packet */
195 while (p->pos < p->size) {
196 res = recv(this->sock, (char*)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 this->CloseConnection();
203 return NULL;
205 /* Connection would block */
206 return NULL;
208 if (res == 0) {
209 /* Client/server has left */
210 this->CloseConnection();
211 return NULL;
214 p->pos += res;
217 /* Prepare for receiving a new packet */
218 this->packet_recv = NULL;
220 p->PrepareToRead();
221 return p;
225 * Check whether this socket can send or receive something.
226 * @return \c true when there is something to receive.
227 * @note Sets #writable if more data can be sent.
229 bool NetworkTCPSocketHandler::CanSendReceive()
231 fd_set read_fd, write_fd;
232 struct timeval tv;
234 FD_ZERO(&read_fd);
235 FD_ZERO(&write_fd);
237 FD_SET(this->sock, &read_fd);
238 FD_SET(this->sock, &write_fd);
240 tv.tv_sec = tv.tv_usec = 0; // don't block at all.
241 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
242 if (select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv) < 0) return false;
243 #else
244 if (WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL) < 0) return false;
245 #endif
247 this->writable = !!FD_ISSET(this->sock, &write_fd);
248 return FD_ISSET(this->sock, &read_fd) != 0;
251 #endif /* ENABLE_NETWORK */