|
1 /* $Id$ */ |
|
2 |
|
3 #ifdef ENABLE_NETWORK |
|
4 |
|
5 #include "../../stdafx.h" |
|
6 #include "../../debug.h" |
|
7 #include "../../openttd.h" |
|
8 #include "../../variables.h" |
|
9 #include "table/strings.h" |
|
10 #include "../../functions.h" |
|
11 |
|
12 #include "os_abstraction.h" |
|
13 #include "config.h" |
|
14 #include "packet.h" |
|
15 #include "../network_data.h" |
|
16 #include "tcp.h" |
|
17 |
|
18 /** |
|
19 * @file tcp.c Basic functions to receive and send TCP packets. |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit |
|
24 * A socket can make errors. When that happens this handles what to do. |
|
25 * For clients: close connection and drop back to main-menu |
|
26 * For servers: close connection and that is it |
|
27 * @param cs the client to close the connection of |
|
28 * @return the new status |
|
29 */ |
|
30 NetworkRecvStatus CloseConnection(NetworkClientState *cs) |
|
31 { |
|
32 NetworkCloseClient(cs); |
|
33 |
|
34 /* Clients drop back to the main menu */ |
|
35 if (!_network_server && _networking) { |
|
36 _switch_mode = SM_MENU; |
|
37 _networking = false; |
|
38 _switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION; |
|
39 |
|
40 return NETWORK_RECV_STATUS_CONN_LOST; |
|
41 } |
|
42 |
|
43 return NETWORK_RECV_STATUS_OKAY; |
|
44 } |
|
45 |
|
46 /** |
|
47 * Whether the client has quit or not (used in packet.c) |
|
48 * @param cs the client to check |
|
49 * @return true if the client has quit |
|
50 */ |
|
51 bool HasClientQuit(NetworkClientState *cs) |
|
52 { |
|
53 return cs->has_quit; |
|
54 } |
|
55 |
|
56 /** |
|
57 * This function puts the packet in the send-queue and it is send as |
|
58 * soon as possible. This is the next tick, or maybe one tick later |
|
59 * if the OS-network-buffer is full) |
|
60 * @param packet the packet to send |
|
61 * @param cs the client to send to |
|
62 */ |
|
63 void NetworkSend_Packet(Packet *packet, NetworkClientState *cs) |
|
64 { |
|
65 Packet *p; |
|
66 assert(packet != NULL); |
|
67 |
|
68 packet->pos = 0; |
|
69 packet->next = NULL; |
|
70 |
|
71 NetworkSend_FillPacketSize(packet); |
|
72 |
|
73 /* Locate last packet buffered for the client */ |
|
74 p = cs->packet_queue; |
|
75 if (p == NULL) { |
|
76 /* No packets yet */ |
|
77 cs->packet_queue = packet; |
|
78 } else { |
|
79 /* Skip to the last packet */ |
|
80 while (p->next != NULL) p = p->next; |
|
81 p->next = packet; |
|
82 } |
|
83 } |
|
84 |
|
85 /** |
|
86 * Sends all the buffered packets out for this client. It stops when: |
|
87 * 1) all packets are send (queue is empty) |
|
88 * 2) the OS reports back that it can not send any more |
|
89 * data right now (full network-buffer, it happens ;)) |
|
90 * 3) sending took too long |
|
91 * @param cs the client to send the packets for |
|
92 */ |
|
93 bool NetworkSend_Packets(NetworkClientState *cs) |
|
94 { |
|
95 ssize_t res; |
|
96 Packet *p; |
|
97 |
|
98 /* We can not write to this socket!! */ |
|
99 if (!cs->writable) return false; |
|
100 if (cs->socket == INVALID_SOCKET) return false; |
|
101 |
|
102 p = cs->packet_queue; |
|
103 while (p != NULL) { |
|
104 res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0); |
|
105 if (res == -1) { |
|
106 int err = GET_LAST_ERROR(); |
|
107 if (err != EWOULDBLOCK) { |
|
108 /* Something went wrong.. close client! */ |
|
109 DEBUG(net, 0, "send failed with error %d", err); |
|
110 CloseConnection(cs); |
|
111 return false; |
|
112 } |
|
113 return true; |
|
114 } |
|
115 if (res == 0) { |
|
116 /* Client/server has left us :( */ |
|
117 CloseConnection(cs); |
|
118 return false; |
|
119 } |
|
120 |
|
121 p->pos += res; |
|
122 |
|
123 /* Is this packet sent? */ |
|
124 if (p->pos == p->size) { |
|
125 /* Go to the next packet */ |
|
126 cs->packet_queue = p->next; |
|
127 free(p); |
|
128 p = cs->packet_queue; |
|
129 } else { |
|
130 return true; |
|
131 } |
|
132 } |
|
133 |
|
134 return true; |
|
135 } |
|
136 |
|
137 /** |
|
138 * Receives a packet for the given client |
|
139 * @param cs the client to (try to) receive a packet for |
|
140 * @param status the variable to store the status into |
|
141 * @return the received packet (or NULL when it didn't receive one) |
|
142 */ |
|
143 Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status) |
|
144 { |
|
145 ssize_t res; |
|
146 Packet *p; |
|
147 |
|
148 *status = NETWORK_RECV_STATUS_OKAY; |
|
149 |
|
150 if (cs->socket == INVALID_SOCKET) return NULL; |
|
151 |
|
152 if (cs->packet_recv == NULL) { |
|
153 cs->packet_recv = malloc(sizeof(Packet)); |
|
154 if (cs->packet_recv == NULL) error("Failed to allocate packet"); |
|
155 /* Set pos to zero! */ |
|
156 cs->packet_recv->pos = 0; |
|
157 cs->packet_recv->size = 0; // Can be ommited, just for safety reasons |
|
158 } |
|
159 |
|
160 p = cs->packet_recv; |
|
161 |
|
162 /* Read packet size */ |
|
163 if (p->pos < sizeof(PacketSize)) { |
|
164 while (p->pos < sizeof(PacketSize)) { |
|
165 /* Read the size of the packet */ |
|
166 res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0); |
|
167 if (res == -1) { |
|
168 int err = GET_LAST_ERROR(); |
|
169 if (err != EWOULDBLOCK) { |
|
170 /* Something went wrong... (104 is connection reset by peer) */ |
|
171 if (err != 104) DEBUG(net, 0, "recv failed with error %d", err); |
|
172 *status = CloseConnection(cs); |
|
173 return NULL; |
|
174 } |
|
175 /* Connection would block, so stop for now */ |
|
176 return NULL; |
|
177 } |
|
178 if (res == 0) { |
|
179 /* Client/server has left */ |
|
180 *status = CloseConnection(cs); |
|
181 return NULL; |
|
182 } |
|
183 p->pos += res; |
|
184 } |
|
185 |
|
186 NetworkRecv_ReadPacketSize(p); |
|
187 |
|
188 if (p->size > SEND_MTU) { |
|
189 *status = CloseConnection(cs); |
|
190 return NULL; |
|
191 } |
|
192 } |
|
193 |
|
194 /* Read rest of packet */ |
|
195 while (p->pos < p->size) { |
|
196 res = recv(cs->socket, 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 *status = CloseConnection(cs); |
|
203 return NULL; |
|
204 } |
|
205 /* Connection would block */ |
|
206 return NULL; |
|
207 } |
|
208 if (res == 0) { |
|
209 /* Client/server has left */ |
|
210 *status = CloseConnection(cs); |
|
211 return NULL; |
|
212 } |
|
213 |
|
214 p->pos += res; |
|
215 } |
|
216 |
|
217 /* We have a complete packet, return it! */ |
|
218 p->pos = 2; |
|
219 p->next = NULL; // Should not be needed, but who knows... |
|
220 |
|
221 /* Prepare for receiving a new packet */ |
|
222 cs->packet_recv = NULL; |
|
223 |
|
224 return p; |
|
225 } |
|
226 |
|
227 #endif /* ENABLE_NETWORK */ |