2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
9 * @file tcp.cpp Basic functions to receive and send TCP packets.
12 #include "../../stdafx.h"
13 #include "../../debug.h"
17 #include "../../safeguards.h"
20 * Construct a socket handler for a TCP connection.
21 * @param s The just opened TCP connection.
23 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s
) :
24 NetworkSocketHandler(),
25 sock(s
), writable(false)
29 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
35 * Close the actual socket of the connection.
36 * Please make sure CloseConnection is called before CloseSocket, as
37 * otherwise not all resources might be released.
39 void NetworkTCPSocketHandler::CloseSocket()
41 if (this->sock
!= INVALID_SOCKET
) closesocket(this->sock
);
42 this->sock
= INVALID_SOCKET
;
46 * This will put this socket handler in a close state. It will not
47 * actually close the OS socket; use CloseSocket for this.
48 * @param error Whether we quit under an error condition or not.
49 * @return new status of the connection.
51 NetworkRecvStatus
NetworkTCPSocketHandler::CloseConnection([[maybe_unused
]] bool error
)
54 this->writable
= false;
56 this->packet_queue
.clear();
57 this->packet_recv
= nullptr;
59 return NETWORK_RECV_STATUS_OKAY
;
63 * This function puts the packet in the send-queue and it is send as
64 * soon as possible. This is the next tick, or maybe one tick later
65 * if the OS-network-buffer is full)
66 * @param packet the packet to send
68 void NetworkTCPSocketHandler::SendPacket(std::unique_ptr
<Packet
> &&packet
)
70 assert(packet
!= nullptr);
72 packet
->PrepareToSend();
73 this->packet_queue
.push_back(std::move(packet
));
77 * Sends all the buffered packets out for this client. It stops when:
78 * 1) all packets are send (queue is empty)
79 * 2) the OS reports back that it can not send any more
80 * data right now (full network-buffer, it happens ;))
81 * 3) sending took too long
82 * @param closing_down Whether we are closing down the connection.
83 * @return \c true if a (part of a) packet could be sent and
84 * the connection is not closed yet.
86 SendPacketsState
NetworkTCPSocketHandler::SendPackets(bool closing_down
)
88 /* We can not write to this socket!! */
89 if (!this->writable
) return SPS_NONE_SENT
;
90 if (!this->IsConnected()) return SPS_CLOSED
;
92 while (!this->packet_queue
.empty()) {
93 Packet
&p
= *this->packet_queue
.front();
94 ssize_t res
= p
.TransferOut
<int>(send
, this->sock
, 0);
96 NetworkError err
= NetworkError::GetLast();
97 if (!err
.WouldBlock()) {
98 /* Something went wrong.. close client! */
100 Debug(net
, 0, "Send failed: {}", err
.AsString());
101 this->CloseConnection();
105 return SPS_PARTLY_SENT
;
108 /* Client/server has left us :( */
109 if (!closing_down
) this->CloseConnection();
113 /* Is this packet sent? */
114 if (p
.RemainingBytesToTransfer() == 0) {
115 /* Go to the next packet */
116 this->packet_queue
.pop_front();
118 return SPS_PARTLY_SENT
;
126 * Receives a packet for the given client
127 * @return The received packet (or nullptr when it didn't receive one)
129 std::unique_ptr
<Packet
> NetworkTCPSocketHandler::ReceivePacket()
133 if (!this->IsConnected()) return nullptr;
135 if (this->packet_recv
== nullptr) {
136 this->packet_recv
= std::make_unique
<Packet
>(this, TCP_MTU
);
139 Packet
&p
= *this->packet_recv
.get();
141 /* Read packet size */
142 if (!p
.HasPacketSizeData()) {
143 while (p
.RemainingBytesToTransfer() != 0) {
144 res
= p
.TransferIn
<int>(recv
, this->sock
, 0);
146 NetworkError err
= NetworkError::GetLast();
147 if (!err
.WouldBlock()) {
148 /* Something went wrong... */
149 if (!err
.IsConnectionReset()) Debug(net
, 0, "Recv failed: {}", err
.AsString());
150 this->CloseConnection();
153 /* Connection would block, so stop for now */
157 /* Client/server has left */
158 this->CloseConnection();
163 /* Parse the size in the received packet and if not valid, close the connection. */
164 if (!p
.ParsePacketSize()) {
165 this->CloseConnection();
170 /* Read rest of packet */
171 while (p
.RemainingBytesToTransfer() != 0) {
172 res
= p
.TransferIn
<int>(recv
, this->sock
, 0);
174 NetworkError err
= NetworkError::GetLast();
175 if (!err
.WouldBlock()) {
176 /* Something went wrong... */
177 if (!err
.IsConnectionReset()) Debug(net
, 0, "Recv failed: {}", err
.AsString());
178 this->CloseConnection();
181 /* Connection would block */
185 /* Client/server has left */
186 this->CloseConnection();
191 if (!p
.PrepareToRead()) {
192 Debug(net
, 0, "Invalid packet received (too small / decryption error)");
193 this->CloseConnection();
196 return std::move(this->packet_recv
);
200 * Check whether this socket can send or receive something.
201 * @return \c true when there is something to receive.
202 * @note Sets #writable if more data can be sent.
204 bool NetworkTCPSocketHandler::CanSendReceive()
206 assert(this->sock
!= INVALID_SOCKET
);
208 fd_set read_fd
, write_fd
;
214 FD_SET(this->sock
, &read_fd
);
215 FD_SET(this->sock
, &write_fd
);
217 tv
.tv_sec
= tv
.tv_usec
= 0; // don't block at all.
218 if (select(FD_SETSIZE
, &read_fd
, &write_fd
, nullptr, &tv
) < 0) return false;
220 this->writable
= !!FD_ISSET(this->sock
, &write_fd
);
221 return FD_ISSET(this->sock
, &read_fd
) != 0;