Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

always bind to sockets, even in client mode

Maybe the WSAEINVAL on Windows goes away with this.

TODO
Check if there are any side effects of this action.
Are there any half-opened connections, if someone tries to connect to
the client?
Surely there is an useless bind on one of the two socket wich is not
used.

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