OpenTTD Source 20241224-master-gf74b0cf984
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
25 sock(s), writable(false)
26{
27}
28
29NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
30{
31 this->CloseSocket();
32}
33
40{
41 if (this->sock != INVALID_SOCKET) closesocket(this->sock);
42 this->sock = INVALID_SOCKET;
43}
44
52{
53 this->MarkClosed();
54 this->writable = false;
55
56 this->packet_queue.clear();
57 this->packet_recv = nullptr;
58
60}
61
68void NetworkTCPSocketHandler::SendPacket(std::unique_ptr<Packet> &&packet)
69{
70 assert(packet != nullptr);
71
72 packet->PrepareToSend();
73 this->packet_queue.push_back(std::move(packet));
74}
75
87{
88 /* We can not write to this socket!! */
89 if (!this->writable) return SPS_NONE_SENT;
90 if (!this->IsConnected()) return SPS_CLOSED;
91
92 while (!this->packet_queue.empty()) {
93 Packet &p = *this->packet_queue.front();
94 ssize_t res = p.TransferOut<int>(send, this->sock, 0);
95 if (res == -1) {
97 if (!err.WouldBlock()) {
98 /* Something went wrong.. close client! */
99 if (!closing_down) {
100 Debug(net, 0, "Send failed: {}", err.AsString());
101 this->CloseConnection();
102 }
103 return SPS_CLOSED;
104 }
105 return SPS_PARTLY_SENT;
106 }
107 if (res == 0) {
108 /* Client/server has left us :( */
109 if (!closing_down) this->CloseConnection();
110 return SPS_CLOSED;
111 }
112
113 /* Is this packet sent? */
114 if (p.RemainingBytesToTransfer() == 0) {
115 /* Go to the next packet */
116 this->packet_queue.pop_front();
117 } else {
118 return SPS_PARTLY_SENT;
119 }
120 }
121
122 return SPS_ALL_SENT;
123}
124
130{
131 ssize_t res;
132
133 if (!this->IsConnected()) return nullptr;
134
135 if (this->packet_recv == nullptr) {
136 this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
137 }
138
139 Packet &p = *this->packet_recv.get();
140
141 /* Read packet size */
142 if (!p.HasPacketSizeData()) {
143 while (p.RemainingBytesToTransfer() != 0) {
144 res = p.TransferIn<int>(recv, this->sock, 0);
145 if (res == -1) {
147 if (!err.WouldBlock()) {
148 /* Something went wrong... */
149 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
150 this->CloseConnection();
151 return nullptr;
152 }
153 /* Connection would block, so stop for now */
154 return nullptr;
155 }
156 if (res == 0) {
157 /* Client/server has left */
158 this->CloseConnection();
159 return nullptr;
160 }
161 }
162
163 /* Parse the size in the received packet and if not valid, close the connection. */
164 if (!p.ParsePacketSize()) {
165 this->CloseConnection();
166 return nullptr;
167 }
168 }
169
170 /* Read rest of packet */
171 while (p.RemainingBytesToTransfer() != 0) {
172 res = p.TransferIn<int>(recv, this->sock, 0);
173 if (res == -1) {
175 if (!err.WouldBlock()) {
176 /* Something went wrong... */
177 if (!err.IsConnectionReset()) Debug(net, 0, "Recv failed: {}", err.AsString());
178 this->CloseConnection();
179 return nullptr;
180 }
181 /* Connection would block */
182 return nullptr;
183 }
184 if (res == 0) {
185 /* Client/server has left */
186 this->CloseConnection();
187 return nullptr;
188 }
189 }
190
191 if (!p.PrepareToRead()) {
192 Debug(net, 0, "Invalid packet received (too small / decryption error)");
193 this->CloseConnection();
194 return nullptr;
195 }
196 return std::move(this->packet_recv);
197}
198
205{
206 assert(this->sock != INVALID_SOCKET);
207
208 fd_set read_fd, write_fd;
209 struct timeval tv;
210
211 FD_ZERO(&read_fd);
212 FD_ZERO(&write_fd);
213
214 FD_SET(this->sock, &read_fd);
215 FD_SET(this->sock, &write_fd);
216
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;
219
220 this->writable = !!FD_ISSET(this->sock, &write_fd);
221 return FD_ISSET(this->sock, &read_fd) != 0;
222}
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.
SocketHandler for all network sockets in OpenTTD.
Definition core.h:43
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:51
virtual std::unique_ptr< Packet > ReceivePacket()
Receives a packet for the given client.
Definition tcp.cpp:129
bool IsConnected() const
Whether this socket is currently bound to a socket.
Definition tcp.h:45
NetworkTCPSocketHandler(SOCKET s=INVALID_SOCKET)
Construct a socket handler for a TCP connection.
Definition tcp.cpp:23
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:68
SendPacketsState SendPackets(bool closing_down=false)
Sends all the buffered packets out for this client.
Definition tcp.cpp:86
void CloseSocket()
Close the actual socket of the connection.
Definition tcp.cpp:39
bool CanSendReceive()
Check whether this socket can send or receive something.
Definition tcp.cpp:204
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