OpenTTD Source 20250205-master-gfd85ab1e2c
tcp.cpp
Go to the documentation of this file.
1/*
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/>.
6 */
7
12#include "../../stdafx.h"
13#include "../../debug.h"
14
15#include "tcp.h"
16
17#include "../../safeguards.h"
18
19NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
20{
21 this->CloseSocket();
22}
23
30{
31 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
32 this->sock = INVALID_SOCKET;
33}
34
42{
43 this->MarkClosed();
44 this->writable = false;
45
46 this->packet_queue.clear();
47 this->packet_recv = nullptr;
48
50}
51
58void NetworkTCPSocketHandler::SendPacket(std::unique_ptr<Packet> &&packet)
59{
60 assert(packet != nullptr);
61
62 packet->PrepareToSend();
63 this->packet_queue.push_back(std::move(packet));
64}
65
77{
78 /* We can not write to this socket!! */
79 if (!this->writable) return SPS_NONE_SENT;
80 if (!this->IsConnected()) return SPS_CLOSED;
81
82 while (!this->packet_queue.empty()) {
83 Packet &p = *this->packet_queue.front();
84 ssize_t res = p.TransferOut<int>(send, this->sock, 0);
85 if (res == -1) {
87 if (!err.WouldBlock()) {
88 /* Something went wrong.. close client! */
89 if (!closing_down) {
90 Debug(net, 0, "Send failed: {}", err.AsString());
91 this->CloseConnection();
92 }
93 return SPS_CLOSED;
94 }
95 return SPS_PARTLY_SENT;
96 }
97 if (res == 0) {
98 /* Client/server has left us :( */
99 if (!closing_down) this->CloseConnection();
100 return SPS_CLOSED;
101 }
102
103 /* Is this packet sent? */
104 if (p.RemainingBytesToTransfer() == 0) {
105 /* Go to the next packet */
106 this->packet_queue.pop_front();
107 } else {
108 return SPS_PARTLY_SENT;
109 }
110 }
111
112 return SPS_ALL_SENT;
113}
114
120{
121 ssize_t res;
122
123 if (!this->IsConnected()) return nullptr;
124
125 if (this->packet_recv == nullptr) {
126 this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
127 }
128
129 Packet &p = *this->packet_recv.get();
130
131 /* Read packet size */
132 if (!p.HasPacketSizeData()) {
133 while (p.RemainingBytesToTransfer() != 0) {
134 res = p.TransferIn<int>(recv, this->sock, 0);
135 if (res == -1) {
137 if (!err.WouldBlock()) {
138 /* Something went wrong... */
139 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
140 this->CloseConnection();
141 return nullptr;
142 }
143 /* Connection would block, so stop for now */
144 return nullptr;
145 }
146 if (res == 0) {
147 /* Client/server has left */
148 this->CloseConnection();
149 return nullptr;
150 }
151 }
152
153 /* Parse the size in the received packet and if not valid, close the connection. */
154 if (!p.ParsePacketSize()) {
155 this->CloseConnection();
156 return nullptr;
157 }
158 }
159
160 /* Read rest of packet */
161 while (p.RemainingBytesToTransfer() != 0) {
162 res = p.TransferIn<int>(recv, this->sock, 0);
163 if (res == -1) {
165 if (!err.WouldBlock()) {
166 /* Something went wrong... */
167 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
168 this->CloseConnection();
169 return nullptr;
170 }
171 /* Connection would block */
172 return nullptr;
173 }
174 if (res == 0) {
175 /* Client/server has left */
176 this->CloseConnection();
177 return nullptr;
178 }
179 }
180
181 if (!p.PrepareToRead()) {
182 Debug(net, 0, "Invalid packet received (too small / decryption error)");
183 this->CloseConnection();
184 return nullptr;
185 }
186 return std::move(this->packet_recv);
187}
188
195{
196 assert(this->sock != INVALID_SOCKET);
197
198 fd_set read_fd, write_fd;
199 struct timeval tv;
200
201 FD_ZERO(&read_fd);
202 FD_ZERO(&write_fd);
203
204 FD_SET(this->sock, &read_fd);
205 FD_SET(this->sock, &write_fd);
206
207 tv.tv_sec = tv.tv_usec = 0; // don't block at all.
208 if (select(FD_SETSIZE, &read_fd, &write_fd, nullptr, &tv) < 0) return false;
209
210 this->writable = !!FD_ISSET(this->sock, &write_fd);
211 return FD_ISSET(this->sock, &read_fd) != 0;
212}
Abstraction of a network error where all implementation details of the error codes are encapsulated i...
bool WouldBlock() const
Check whether this error describes that the operation would block.
bool IsConnectionReset() const
Check whether this error describes a connection reset.
const std::string & AsString() const
Get the string representation of the error message.
static NetworkError GetLast()
Get the last network error.
void MarkClosed()
Mark the connection as closed.
Definition core.h:66
std::deque< std::unique_ptr< Packet > > packet_queue
Packets that are awaiting delivery. Cannot be std::queue as that does not have a clear() function.
Definition tcp.h:33
virtual NetworkRecvStatus CloseConnection(bool error=true)
This will put this socket handler in a close state.
Definition tcp.cpp:41
virtual std::unique_ptr< Packet > ReceivePacket()
Receives a packet for the given client.
Definition tcp.cpp:119
bool IsConnected() const
Whether this socket is currently bound to a socket.
Definition tcp.h:45
SOCKET sock
The socket currently connected to.
Definition tcp.h:38
bool writable
Can we write to this socket?
Definition tcp.h:39
virtual void SendPacket(std::unique_ptr< Packet > &&packet)
This function puts the packet in the send-queue and it is send as soon as possible.
Definition tcp.cpp:58
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:76
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:29
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:194
std::unique_ptr< Packet > packet_recv
Partially received packet.
Definition tcp.h:34
static const size_t TCP_MTU
Number of bytes we can pack in a single TCP packet.
Definition config.h:45
NetworkRecvStatus
Status of a network client; reasons why a client has quit.
Definition core.h:23
@ NETWORK_RECV_STATUS_OKAY
Everything is okay.
Definition core.h:24
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
Internal entity of a packet.
Definition packet.h:42
bool PrepareToRead()
Prepares the packet so it can be read.
Definition packet.cpp:278
bool HasPacketSizeData() const
Check whether the packet, given the position of the "write" pointer, has read enough of the packet to...
Definition packet.cpp:238
bool ParsePacketSize()
Reads the packet size from the raw packet and stores it in the packet->size.
Definition packet.cpp:259
ssize_t TransferIn(F transfer_function, S source, Args &&... args)
Transfer data from the given function into the packet.
Definition packet.h:174
size_t RemainingBytesToTransfer() const
Get the amount of bytes that are still available for the Transfer functions.
Definition packet.cpp:447
ssize_t TransferOut(F transfer_function, D destination, Args &&... args)
Transfer data from the packet to the given function.
Definition packet.h:139
Basic functions to receive and send TCP packets.
SendPacketsState
The states of sending the packets.
Definition tcp.h:23
@ SPS_PARTLY_SENT
The packets are partly sent; there are more packets to be sent in the queue.
Definition tcp.h:26
@ SPS_ALL_SENT
All packets in the queue are sent.
Definition tcp.h:27
@ SPS_NONE_SENT
The buffer is still full, so no (parts of) packets could be sent.
Definition tcp.h:25
@ SPS_CLOSED
The connection got closed.
Definition tcp.h:24