码迷,mamicode.com
首页 > Web开发 > 详细

通向码农的道路(enet开源翻译计划 一)

时间:2015-07-11 01:09:39      阅读:237      评论:0      收藏:0      [点我收藏+]

标签:

Features:
    ENet evolved specifically as a UDP networking layer for the multiplayer first person shooter Cube.
ENet 最初衷设计为了第一人称射击类游戏。为什么需要udp (参考,unix网络编程,如果不是为了进行多播,不要使用udp,我们应该使用tcp,让厂商来关注性能 )因为无法容忍延迟。(参考tcp v1)


目前我们游戏来看卡牌塔防类,根本不需要udp  就算有高延迟玩家有什么不能接受的, 看看梦幻细雨,有时候一回合延迟高达10秒??  最终我们失去了tcp的重新分组,快速恢复算法,快速重传,延迟确认机制 ,坚持定时器........ 

因为看到腾讯的 t3 面试问题 如何提网络吞吐量?enet 正是如何回答这个问题的最佳方案,我想enet 正是学习tcp最佳路程,如果只是看内核tcp源码学习,难免会有点不自量力。 

1.
     
enet_host_create( &address, 4095200 );  // num of clients, num of channels, incoming bandwidth, outgoing bandwidth。


 channels  这个设计目前我理解的为了进行负载均衡, 比如 client    -》 zoneconnect     -》 zoneserver   
如果此时  client zoneconnect  1 num   ,
zoneconnect  zoneserver   2  num ,     1 num  之间通信发生了拥塞,此时通过拥塞控制,减少发包频率,如果 2  num 也在发包呢?  然而此时的 1  num 已经发生了拥塞 ,那么我们的所需要发送的数据包,只有异步等待 1 num发送  为了解决这个问题的延迟,并减少对数据包的限制,使用多通道独立的进行发送,因此此时一个通道的数据包传送状态不会影响其他通道的包传送 。
 如果默认为0 则是禁止开启流量控制,和拥塞控制。(参考 tcpv2 内核源码)


    To combat this latency and reduce the ordering restrictions on packets, ENet provides multiple channels of communication over a given connection. Each channel is independently sequenced, and so the delivery status of a packet in one channel will not stall the delivery of other packets in another channel.

 
ENetHost *

enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth,enet_uint32 outgoingBandwidth)

{

    ENetHost * host; //服务器本端 一个全局变量存储数据

    ENetPeer * currentPeer;//当前客户端就是一个peer 

//ENET_PROTOCOL_MAXIMUM_PEER_ID

//

    if( peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID ) 

      return NULL;


    host = (ENetHost *) enet_malloc (sizeof (ENetHost));

    if (host == NULL)

      return NULL;

    memset(host, 0sizeof (ENetHost));


    host ->peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer));

    if (host ->peers == NULL)

    {

       enet_free (host);


       return NULL;

    }

    memset (host ->peers0, peerCount * sizeof (ENetPeer));


    host ->socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM);

    if (host ->socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host ->socket, address) < 0))

    {

       if (host ->socket != ENET_SOCKET_NULL)

         enet_socket_destroy (host ->socket);


       enet_free (host ->peers);

       enet_free (host);


       return NULL;

    }


    enet_socket_set_option (host ->socketENET_SOCKOPT_NONBLOCK1); //设置非阻塞

    enet_socket_set_option (host ->socketENET_SOCKOPT_BROADCAST1);//设置广播 

    enet_socket_set_option (host ->socketENET_SOCKOPT_RCVBUFENET_HOST_RECEIVE_BUFFER_SIZE); //设置socket 接受缓冲区

    enet_socket_set_option (host ->socketENET_SOCKOPT_SNDBUFENET_HOST_SEND_BUFFER_SIZE);//设置socket发送缓冲区


    if (address != NULL && enet_socket_get_address (host ->socket, & host -> address) < 0 // 绑定socket  设置地址

      host -> address = * address;


    if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT)

      channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT;

    else

    if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT)

      channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT;


    host ->randomSeed = (enet_uint32) (size_t) host;

    host ->randomSeed += enet_host_random_seed ();

    host ->randomSeed = (host ->randomSeed << 16) | (host ->randomSeed >> 16);

    host ->channelLimit = channelLimit;

    host ->incomingBandwidth = incomingBandwidth;

    host ->outgoingBandwidth = outgoingBandwidth;

    host ->bandwidthThrottleEpoch = 0;

    host ->recalculateBandwidthLimits = 0;

    host ->mtu = ENET_HOST_DEFAULT_MTU//通信最大包限制, 本身自带分包发送。

    host ->peerCount = peerCount;

    host ->commandCount = 0;

    host ->bufferCount = 0;

    host ->checksum = NULL;

    host ->receivedAddress.host = ENET_HOST_ANY;

    host ->receivedAddress.port = 0;

    host ->receivedData = NULL;

    host ->receivedDataLength = 0;

     

    host ->totalSentData = 0;

    host ->totalSentPackets = 0;

    host ->totalReceivedData = 0;

    host ->totalReceivedPackets = 0;


    host ->connectedPeers = 0;

    host ->bandwidthLimitedPeers = 0;

    host ->duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID;

    host ->maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE;

    host ->maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA;


    host ->compressor.context = NULL// 这里开启压缩包算法

    host ->compressor.compress = NULL;

    host ->compressor.decompress = NULL;

    host ->compressor.destroy = NULL;


    host ->intercept = NULL;


    enet_list_clear (& host ->dispatchQueue); //双向链表 每次必须clear 使得双指针指向头节点


    for (currentPeer = host ->peers;

         currentPeer < & host ->peers [host -> peerCount];

         ++ currentPeer)

    {

       currentPeer ->host = host;

       currentPeer ->incomingPeerID = currentPeer - host ->peers;

       currentPeer ->outgoingSessionID = currentPeer ->incomingSessionID = 0xFF;

       currentPeer ->data = NULL;


       enet_list_clear (& currentPeer ->acknowledgements);

       enet_list_clear (& currentPeer ->sentReliableCommands);

       enet_list_clear (& currentPeer ->sentUnreliableCommands);

       enet_list_clear (& currentPeer ->outgoingReliableCommands);

       enet_list_clear (& currentPeer ->outgoingUnreliableCommands);

       enet_list_clear (& currentPeer ->dispatchedCommands);


       enet_peer_reset (currentPeer);

    }


    return host;

}

2.

 
 /** Forcefully disconnects a peer.

    @param peer peer to forcefully disconnect

    @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout

    on its connection to the local host.

*/

void

enet_peer_reset (ENetPeer * peer)

{

    enet_peer_on_disconnect (peer);

        

    peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID;

    peer -> connectID = 0;


    peer -> state = ENET_PEER_STATE_DISCONNECTED;


    peer -> incomingBandwidth = 0;

    peer -> outgoingBandwidth = 0;

    peer -> incomingBandwidthThrottleEpoch = 0;

    peer -> outgoingBandwidthThrottleEpoch = 0;

    peer -> incomingDataTotal = 0;

    peer -> outgoingDataTotal = 0;

    peer -> lastSendTime = 0;

    peer -> lastReceiveTime = 0;

    peer -> nextTimeout = 0;   客户端下次超时时间

    peer -> earliestTimeout = 0;  最早的超时时间

    peer -> packetLossEpoch = 0;

    peer -> packetsSent = 0;

    peer -> packetsLost = 0;

    peer -> packetLoss = 0;

    peer -> packetLossVariance = 0;

    peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE;

    peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE;

    peer -> packetThrottleCounter = 0;

    peer -> packetThrottleEpoch = 0;

    peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION;

    peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION;

    peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL;

    peer -> pingInterval = ENET_PEER_PING_INTERVAL;    心跳检测时间

    peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT;    

    peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM;    

    peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM;  最大超时时间  如果没有收到对端的ack确认,会一直重传,至道最大超时,并且踢掉玩家

    peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;   

    peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;

    peer -> lastRoundTripTimeVariance = 0;

    peer -> highestRoundTripTimeVariance = 0;

    peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME;  客户端和服务器通信往返时间设置,用于判断是否超时

    peer -> roundTripTimeVariance = 0;

    peer -> mtu = peer -> host -> mtu;

    peer -> reliableDataInTransit = 0;

    peer -> outgoingReliableSequenceNumber = 0;

    peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; 滑动窗口大小

    peer -> incomingUnsequencedGroup = 0;

    peer -> outgoingUnsequencedGroup = 0;

    peer -> eventData = 0;

    peer -> totalWaitingData = 0;


    memset (peer -> unsequencedWindow0sizeof (peer -> unsequencedWindow));

    

    enet_peer_reset_queues (peer);

}


3.

void

enet_peer_reset_queues (ENetPeer * peer)

{

    ENetChannel * channel;


    if (peer -> needsDispatch

    {

       enet_list_remove (& peer -> dispatchList); 收到收到的数据包都会加入到调度队列


       peer -> needsDispatch = 0;

    }


    while (! enet_list_empty (& peer -> acknowledgements))  对端确认协议

      enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements)));


    enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); 可靠数据包协议 (每次发送send包后,需要存储到这个双向链表,目的在于存储,用于超时重传,只有收到ack确认,才会删除)

    enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); 不可靠数据包协议

    enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands);   送数据所有都会优先加入到 进来的可靠数据包协议 然后send后,又会加入到peer -> sentReliableCommands  如果检测超时,又会从新回到peer -> outgoingReliableCommands

    enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands);

    enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); 调度协议


    if (peer -> channels != NULL && peer -> channelCount > 0初始化通道

    {

        for (channel = peer -> channels;

             channel < & peer -> channels [peer -> channelCount];

             ++ channel)

        {

            enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands);

            enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands);

        }


        enet_free (peer -> channels);

    }


    peer -> channels = NULL;

    peer -> channelCount = 0;

} 


版权声明:本文为博主原创文章,未经博主允许不得转载。

通向码农的道路(enet开源翻译计划 一)

标签:

原文地址:http://blog.csdn.net/a474711079/article/details/46836495

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!