OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

tcp.cpp
Go to the documentation of this file.
1 /* $Id: tcp.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2
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 */
9
14 #ifdef ENABLE_NETWORK
15
16 #include "../../stdafx.h"
17 #include "../../debug.h"
18
19 #include "tcp.h"
20
21 #include "../../safeguards.h"
22
27 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
28 NetworkSocketHandler(),
29 packet_queue(NULL), packet_recv(NULL),
30 sock(s), writable(false)
31 {
32 }
33
34 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
35 {
36 this->CloseConnection();
37
38 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
39 this->sock = INVALID_SOCKET;
40 }
41
42 NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
43 {
44 this->writable = false;
45 NetworkSocketHandler::CloseConnection(error);
46
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;
52 }
53 delete this->packet_recv;
54 this->packet_recv = NULL;
55
56 return NETWORK_RECV_STATUS_OKAY;
57 }
58
65 void NetworkTCPSocketHandler::SendPacket(Packet *packet)
66 {
67 Packet *p;
68 assert(packet != NULL);
69
70 packet->PrepareToSend();
71
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);
76
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;
86 }
87 }
88
99 SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
100 {
101 ssize_t res;
102 Packet *p;
103
104 /* We can not write to this socket!! */
105 if (!this->writable) return SPS_NONE_SENT;
106 if (!this->IsConnected()) return SPS_CLOSED;
107
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();
118 }
119 return SPS_CLOSED;
120 }
121 return SPS_PARTLY_SENT;
122 }
123 if (res == 0) {
124 /* Client/server has left us :( */
125 if (!closing_down) this->CloseConnection();
126 return SPS_CLOSED;
127 }
128
129 p->pos += res;
130
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;
139 }
140 }
141
142 return SPS_ALL_SENT;
143 }
144
149 Packet *NetworkTCPSocketHandler::ReceivePacket()
150 {
151 ssize_t res;
152
153 if (!this->IsConnected()) return NULL;
154
155 if (this->packet_recv == NULL) {
156 this->packet_recv = new Packet(this);
157 }
158
159 Packet *p = this->packet_recv;
160
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;
173 }
174 /* Connection would block, so stop for now */
175 return NULL;
176 }
177 if (res == 0) {
178 /* Client/server has left */
179 this->CloseConnection();
180 return NULL;
181 }
182 p->pos += res;
183 }
184
185 /* Read the packet size from the received packet */
186 p->ReadRawPacketSize();
187
188 if (p->size > SEND_MTU) {
189 this->CloseConnection();
190 return NULL;
191 }
192 }
193
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;
204 }
205 /* Connection would block */
206 return NULL;
207 }
208 if (res == 0) {
209 /* Client/server has left */
210 this->CloseConnection();
211 return NULL;
212 }
213
214 p->pos += res;
215 }
216
217 /* Prepare for receiving a new packet */
218 this->packet_recv = NULL;
219
220 p->PrepareToRead();
221 return p;
222 }
223
229 bool NetworkTCPSocketHandler::CanSendReceive()
230 {
231 fd_set read_fd, write_fd;
232 struct timeval tv;
233
234 FD_ZERO(&read_fd);
235 FD_ZERO(&write_fd);
236
237 FD_SET(this->sock, &read_fd);
238 FD_SET(this->sock, &write_fd);
239
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
246
247 this->writable = !!FD_ISSET(this->sock, &write_fd);
248 return FD_ISSET(this->sock, &read_fd) != 0;
249 }
250
251 #endif /* ENABLE_NETWORK */