zoukankan      html  css  js  c++  java
  • 通向码农的道路(enet开源翻译计划 一)

    QQ 324186207群 enet交流技术。主要是为了研究tcp内部执行机制,欢迎大家增加探讨。小弟水平有限。翻译难免有误。


    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; //server本端 一个全局变量存储数据

        ENetPeer * currentPeer;//当前client就是一个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;   client下次超时时间

        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;  client和server通信往返时间设置,用于推断是否超时

        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;

    } 


  • 相关阅读:
    设置EntityFramework中decimal类型数据精度
    关于vue中的nextTick深入理解
    解决使用VS2013创建的MVC项目在VS2015中打开的各种问题
    mysql免安装版的下载与安装
    酷狗缓存文件kgtemp的加密方式
    PowerDesigner反向生成物理数据模型
    VISUAL STUDIO 2012下的OPENCV 2.4.7安装过程
    讲解DLL内容的比较详细的站点
    strcpy_s与strcpy的区别
    【原创】在VS2012中采用C++中调用DLL中的函数(4)
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5232613.html
Copyright © 2011-2022 走看看