1 /**
2 @file host.c
3 @brief ENet host management functions
4 */
5 #define ENET_BUILDING_LIB 1
6 #include <string.h>
7 #include "enet/enet.h"
8
9 /** @defgroup host ENet host functions
10 @{
11 */
12
13 /** Creates a host for communicating to peers.
14
15 @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host.
16 @param peerCount the maximum number of peers that should be allocated for the host.
17 @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
18 @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
19 @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth.
20
21 @returns the host on success and NULL on failure
22
23 @remarks ENet will strategically drop packets on specific sides of a connection between hosts
24 to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine
25 the window size of a connection which limits the amount of reliable packets that may be in transit
26 at any given time.
27 */
28 ENetHost *
enet_host_create(const ENetAddress * address,size_t peerCount,size_t channelLimit,enet_uint32 incomingBandwidth,enet_uint32 outgoingBandwidth)29 enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
30 {
31 ENetHost * host;
32 ENetPeer * currentPeer;
33 int i;
34
35 if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID)
36 return NULL;
37
38 host = (ENetHost *) enet_malloc (sizeof (ENetHost));
39 if (host == NULL)
40 return NULL;
41 memset (host, 0, sizeof (ENetHost));
42
43 host -> peer_list = (ENetPeer **) enet_malloc (ENET_PROTOCOL_MAXIMUM_PEER_ID * sizeof (ENetPeer*));
44 if (host -> peer_list == NULL)
45 {
46 enet_free (host);
47
48 return NULL;
49 }
50 for (i=0; i < peerCount; i++) {
51 host -> peer_list[i] = enet_malloc (sizeof (ENetPeer));
52 memset (host -> peer_list[i], 0, sizeof (ENetPeer));
53 }
54
55 host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);
56 if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0))
57 {
58 if (host -> socket != ENET_SOCKET_NULL)
59 enet_socket_destroy (host -> socket);
60
61 for (i=0; i < peerCount; i++) {
62 enet_free (host -> peer_list[i]);
63 }
64 enet_free (host -> peer_list);
65 enet_free (host);
66
67 return NULL;
68 }
69
70 enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1);
71 enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1);
72 enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE);
73 enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE);
74
75 if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0)
76 host -> address = * address;
77
78 if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
79 channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
80 else
81 if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
82 channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
83
84 host -> randomSeed = (enet_uint32) (size_t) host;
85 host -> randomSeed += enet_host_random_seed ();
86 host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16);
87 host -> channelLimit = channelLimit;
88 host -> incomingBandwidth = incomingBandwidth;
89 host -> outgoingBandwidth = outgoingBandwidth;
90 host -> bandwidthThrottleEpoch = 0;
91 host -> recalculateBandwidthLimits = 0;
92 host -> mtu = ENET_HOST_DEFAULT_MTU;
93 host -> peerCount = peerCount;
94 host -> commandCount = 0;
95 host -> bufferCount = 0;
96 host -> checksum = NULL;
97 host -> receivedAddress.host = ENET_HOST_ANY;
98 host -> receivedAddress.port = 0;
99 host -> receivedData = NULL;
100 host -> receivedDataLength = 0;
101
102 host -> totalSentData = 0;
103 host -> totalSentPackets = 0;
104 host -> totalReceivedData = 0;
105 host -> totalReceivedPackets = 0;
106
107 host -> connectedPeers = 0;
108 host -> bandwidthLimitedPeers = 0;
109 host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;
110 host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;
111 host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;
112
113 host -> compressor.context = NULL;
114 host -> compressor.compress = NULL;
115 host -> compressor.decompress = NULL;
116 host -> compressor.destroy = NULL;
117
118 host -> intercept = NULL;
119
120 enet_list_clear (& host -> dispatchQueue);
121
122 for (i = 0; i < host -> peerCount; i++ )
123 {
124 currentPeer = host -> peer_list[i];
125 currentPeer -> host = host;
126 currentPeer -> incomingPeerID = i;
127 currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
128 currentPeer -> data = NULL;
129
130 enet_list_clear (& currentPeer -> acknowledgements);
131 enet_list_clear (& currentPeer -> sentReliableCommands);
132 enet_list_clear (& currentPeer -> sentUnreliableCommands);
133 enet_list_clear (& currentPeer -> outgoingReliableCommands);
134 enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
135 enet_list_clear (& currentPeer -> dispatchedCommands);
136
137 enet_peer_reset (currentPeer);
138 }
139
140 return host;
141 }
142
143 /** Destroys the host and all resources associated with it.
144 @param host pointer to the host to destroy
145 */
146 void
enet_host_destroy(ENetHost * host)147 enet_host_destroy (ENetHost * host)
148 {
149 ENetPeer * currentPeer;
150 int i;
151
152 if (host == NULL)
153 return;
154
155 enet_socket_destroy (host -> socket);
156
157 for (i = 0; i < host -> peerCount; i++ )
158 {
159 currentPeer = host -> peer_list[i];
160 enet_peer_reset (currentPeer);
161 enet_free (currentPeer);
162 host -> peer_list[i] = NULL;
163 }
164
165 if (host -> compressor.context != NULL && host -> compressor.destroy)
166 (* host -> compressor.destroy) (host -> compressor.context);
167
168 enet_free (host -> peer_list);
169 enet_free (host);
170 }
171
172 /** Initiates a connection to a foreign host.
173 @param host host seeking the connection
174 @param address destination for the connection
175 @param channelCount number of channels to allocate
176 @param data user data supplied to the receiving host
177 @returns a peer representing the foreign host on success, NULL on failure
178 @remarks The peer returned will have not completed the connection until enet_host_service()
179 notifies of an ENET_EVENT_TYPE_CONNECT event for the peer.
180 */
181 ENetPeer *
enet_host_connect(ENetHost * host,const ENetAddress * address,size_t channelCount,enet_uint32 data)182 enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data)
183 {
184 ENetPeer * currentPeer;
185 ENetChannel * channel;
186 ENetProtocol command;
187 int i;
188
189 if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
190 channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
191 else
192 if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
193 channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
194
195 for (i = 0; i < host -> peerCount; i++ )
196 {
197 currentPeer = host -> peer_list[i];
198
199 if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED)
200 break;
201 }
202
203 if (i >= host -> peerCount)
204 {
205 if (i >= ENET_PROTOCOL_MAXIMUM_PEER_ID)
206 return NULL;
207 host -> peerCount++;
208 host -> peer_list[i] = enet_malloc (sizeof (ENetPeer));
209 memset (host -> peer_list[i], 0, sizeof (ENetPeer));
210 currentPeer = host->peer_list[i];
211 currentPeer -> host = host;
212 currentPeer -> incomingPeerID = i;
213 currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF;
214 currentPeer -> data = NULL;
215
216 enet_list_clear (& currentPeer -> acknowledgements);
217 enet_list_clear (& currentPeer -> sentReliableCommands);
218 enet_list_clear (& currentPeer -> sentUnreliableCommands);
219 enet_list_clear (& currentPeer -> outgoingReliableCommands);
220 enet_list_clear (& currentPeer -> outgoingUnreliableCommands);
221 enet_list_clear (& currentPeer -> dispatchedCommands);
222
223 enet_peer_reset (currentPeer);
224 }
225
226 currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel));
227 if (currentPeer -> channels == NULL)
228 return NULL;
229 currentPeer -> channelCount = channelCount;
230 currentPeer -> state = ENET_PEER_STATE_CONNECTING;
231 currentPeer -> address = * address;
232 currentPeer -> connectID = ++ host -> randomSeed;
233
234 if (host -> outgoingBandwidth == 0)
235 currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
236 else
237 currentPeer -> windowSize = (host -> outgoingBandwidth /
238 ENET_PEER_WINDOW_SIZE_SCALE) *
239 ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
240
241 if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE)
242 currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE;
243 else
244 if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE)
245 currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE;
246
247 for (channel = currentPeer -> channels;
248 channel < & currentPeer -> channels [channelCount];
249 ++ channel)
250 {
251 channel -> outgoingReliableSequenceNumber = 0;
252 channel -> outgoingUnreliableSequenceNumber = 0;
253 channel -> incomingReliableSequenceNumber = 0;
254 channel -> incomingUnreliableSequenceNumber = 0;
255
256 enet_list_clear (& channel -> incomingReliableCommands);
257 enet_list_clear (& channel -> incomingUnreliableCommands);
258
259 channel -> usedReliableWindows = 0;
260 memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows));
261 }
262
263 command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
264 command.header.channelID = 0xFF;
265 command.connect.outgoingPeerID = ENET_HOST_TO_NET_32 (currentPeer -> incomingPeerID);
266 command.connect.incomingSessionID = currentPeer -> incomingSessionID;
267 command.connect.outgoingSessionID = currentPeer -> outgoingSessionID;
268 command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu);
269 command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize);
270 command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount);
271 command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth);
272 command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
273 command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval);
274 command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration);
275 command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration);
276 command.connect.connectID = currentPeer -> connectID;
277 command.connect.data = ENET_HOST_TO_NET_32 (data);
278
279 enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0);
280
281 return currentPeer;
282 }
283
284 /** Queues a packet to be sent to all peers associated with the host.
285 @param host host on which to broadcast the packet
286 @param channelID channel on which to broadcast
287 @param packet packet to broadcast
288 */
289 void
enet_host_broadcast(ENetHost * host,enet_uint8 channelID,ENetPacket * packet)290 enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet)
291 {
292 ENetPeer * currentPeer;
293 int i;
294
295 for (i = 0; i < host -> peerCount; i++ )
296 {
297 currentPeer = host -> peer_list[i];
298
299 if (currentPeer -> state != ENET_PEER_STATE_CONNECTED)
300 continue;
301
302 enet_peer_send (currentPeer, channelID, packet);
303 }
304
305 if (packet -> referenceCount == 0)
306 enet_packet_destroy (packet);
307 }
308
309 /** Sets the packet compressor the host should use to compress and decompress packets.
310 @param host host to enable or disable compression for
311 @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled
312 */
313 void
enet_host_compress(ENetHost * host,const ENetCompressor * compressor)314 enet_host_compress (ENetHost * host, const ENetCompressor * compressor)
315 {
316 if (host -> compressor.context != NULL && host -> compressor.destroy)
317 (* host -> compressor.destroy) (host -> compressor.context);
318
319 if (compressor)
320 host -> compressor = * compressor;
321 else
322 host -> compressor.context = NULL;
323 }
324
325 /** Limits the maximum allowed channels of future incoming connections.
326 @param host host to limit
327 @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT
328 */
329 void
enet_host_channel_limit(ENetHost * host,size_t channelLimit)330 enet_host_channel_limit (ENetHost * host, size_t channelLimit)
331 {
332 if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)
333 channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;
334 else
335 if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)
336 channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;
337
338 host -> channelLimit = channelLimit;
339 }
340
341
342 /** Adjusts the bandwidth limits of a host.
343 @param host host to adjust
344 @param incomingBandwidth new incoming bandwidth
345 @param outgoingBandwidth new outgoing bandwidth
346 @remarks the incoming and outgoing bandwidth parameters are identical in function to those
347 specified in enet_host_create().
348 */
349 void
enet_host_bandwidth_limit(ENetHost * host,enet_uint32 incomingBandwidth,enet_uint32 outgoingBandwidth)350 enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth)
351 {
352 host -> incomingBandwidth = incomingBandwidth;
353 host -> outgoingBandwidth = outgoingBandwidth;
354 host -> recalculateBandwidthLimits = 1;
355 }
356
357 void
enet_host_bandwidth_throttle(ENetHost * host)358 enet_host_bandwidth_throttle (ENetHost * host)
359 {
360 enet_uint32 timeCurrent = enet_time_get (),
361 elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch,
362 peersRemaining = (enet_uint32) host -> connectedPeers,
363 dataTotal = ~0,
364 bandwidth = ~0,
365 throttle = 0,
366 bandwidthLimit = 0;
367 int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0;
368 ENetPeer * peer;
369 ENetProtocol command;
370 int i;
371
372 if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
373 return;
374
375 host -> bandwidthThrottleEpoch = timeCurrent;
376
377 if (peersRemaining == 0)
378 return;
379
380 if (host -> outgoingBandwidth != 0)
381 {
382 dataTotal = 0;
383 bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000;
384
385 for (i = 0; i < host -> peerCount; i++ )
386 {
387 peer = host -> peer_list[i];
388
389 if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
390 continue;
391
392 dataTotal += peer -> outgoingDataTotal;
393 }
394 }
395
396 while (peersRemaining > 0 && needsAdjustment != 0)
397 {
398 needsAdjustment = 0;
399
400 if (dataTotal <= bandwidth)
401 throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
402 else
403 throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
404
405 for (i = 0; i < host -> peerCount; i++ )
406 {
407 enet_uint32 peerBandwidth;
408 peer = host -> peer_list[i];
409
410 if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
411 peer -> incomingBandwidth == 0 ||
412 peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
413 continue;
414
415 peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000;
416 if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth)
417 continue;
418
419 peer -> packetThrottleLimit = (peerBandwidth *
420 ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal;
421
422 if (peer -> packetThrottleLimit == 0)
423 peer -> packetThrottleLimit = 1;
424
425 if (peer -> packetThrottle > peer -> packetThrottleLimit)
426 peer -> packetThrottle = peer -> packetThrottleLimit;
427
428 peer -> outgoingBandwidthThrottleEpoch = timeCurrent;
429
430 peer -> incomingDataTotal = 0;
431 peer -> outgoingDataTotal = 0;
432
433 needsAdjustment = 1;
434 -- peersRemaining;
435 bandwidth -= peerBandwidth;
436 dataTotal -= peerBandwidth;
437 }
438 }
439
440 if (peersRemaining > 0)
441 {
442 if (dataTotal <= bandwidth)
443 throttle = ENET_PEER_PACKET_THROTTLE_SCALE;
444 else
445 throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal;
446
447 for (i = 0; i < host -> peerCount; i++ )
448 {
449 peer = host -> peer_list[i];
450
451 if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
452 peer -> outgoingBandwidthThrottleEpoch == timeCurrent)
453 continue;
454
455 peer -> packetThrottleLimit = throttle;
456
457 if (peer -> packetThrottle > peer -> packetThrottleLimit)
458 peer -> packetThrottle = peer -> packetThrottleLimit;
459
460 peer -> incomingDataTotal = 0;
461 peer -> outgoingDataTotal = 0;
462 }
463 }
464
465 if (host -> recalculateBandwidthLimits)
466 {
467 host -> recalculateBandwidthLimits = 0;
468
469 peersRemaining = (enet_uint32) host -> connectedPeers;
470 bandwidth = host -> incomingBandwidth;
471 needsAdjustment = 1;
472
473 if (bandwidth == 0)
474 bandwidthLimit = 0;
475 else
476 while (peersRemaining > 0 && needsAdjustment != 0)
477 {
478 needsAdjustment = 0;
479 bandwidthLimit = bandwidth / peersRemaining;
480
481 for (i = 0; i < host -> peerCount; i++ )
482 {
483 peer = host -> peer_list[i];
484 if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) ||
485 peer -> incomingBandwidthThrottleEpoch == timeCurrent)
486 continue;
487
488 if (peer -> outgoingBandwidth > 0 &&
489 peer -> outgoingBandwidth >= bandwidthLimit)
490 continue;
491
492 peer -> incomingBandwidthThrottleEpoch = timeCurrent;
493
494 needsAdjustment = 1;
495 -- peersRemaining;
496 bandwidth -= peer -> outgoingBandwidth;
497 }
498 }
499
500 for (i = 0; i < host -> peerCount; i++ )
501 {
502 peer = host -> peer_list[i];
503
504 if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)
505 continue;
506
507 command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE;
508 command.header.channelID = 0xFF;
509 command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth);
510
511 if (peer -> incomingBandwidthThrottleEpoch == timeCurrent)
512 command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth);
513 else
514 command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit);
515
516 enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0);
517 }
518 }
519 }
520
521 /** @} */
522