Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/ipv6/src/external/enet/host.c @ 7382

Last change on this file since 7382 was 7377, checked in by adrfried, 14 years ago

using two separate sockets for ipv4 and ipv6

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