1 /* $Id: tcp.cpp 26046 2013-11-22 21:41:19Z rubidium $ */
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/>.
11 * @file tcp.cpp Basic functions to receive and send TCP packets.
16 #include "../../stdafx.h"
17 #include "../../debug.h"
21 #include "../../safeguards.h"
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
;
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
)
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
;
81 this->packet_queue
= packet
;
83 /* Skip to the last packet */
84 while (p
->next
!= NULL
) p
= p
->next
;
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
)
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
;
110 res
= send(this->sock
, (const char*)p
->buffer
+ p
->pos
, p
->size
- p
->pos
, 0);
112 int err
= GET_LAST_ERROR();
113 if (err
!= EWOULDBLOCK
) {
114 /* Something went wrong.. close client! */
116 DEBUG(net
, 0, "send failed with error %d", err
);
117 this->CloseConnection();
121 return SPS_PARTLY_SENT
;
124 /* Client/server has left us :( */
125 if (!closing_down
) this->CloseConnection();
131 /* Is this packet sent? */
132 if (p
->pos
== p
->size
) {
133 /* Go to the next packet */
134 this->packet_queue
= p
->next
;
136 p
= this->packet_queue
;
138 return SPS_PARTLY_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()
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);
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();
174 /* Connection would block, so stop for now */
178 /* Client/server has left */
179 this->CloseConnection();
185 /* Read the packet size from the received packet */
186 p
->ReadRawPacketSize();
188 if (p
->size
> SEND_MTU
) {
189 this->CloseConnection();
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);
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();
205 /* Connection would block */
209 /* Client/server has left */
210 this->CloseConnection();
217 /* Prepare for receiving a new packet */
218 this->packet_recv
= NULL
;
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
;
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;
244 if (WaitSelect(FD_SETSIZE
, &read_fd
, &write_fd
, NULL
, &tv
, NULL
) < 0) return false;
247 this->writable
= !!FD_ISSET(this->sock
, &write_fd
);
248 return FD_ISSET(this->sock
, &read_fd
) != 0;
251 #endif /* ENABLE_NETWORK */