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, 4095, 2, 0, 0 );
 // 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, 0, sizeof (ENetHost));

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

if (host ->peers == NULL)

{

enet_free (host);

return NULL;

}

memset (host ->peers, 0,
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 ->socket, ENET_SOCKOPT_NONBLOCK, 1); //设置非堵塞

enet_socket_set_option (host ->socket, ENET_SOCKOPT_BROADCAST, 1);//设置广播

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

enet_socket_set_option (host ->socket, ENET_SOCKOPT_SNDBUF, ENET_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 -> unsequencedWindow, 0, sizeof (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开源翻译计划 一)的更多相关文章

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

    QQ 324186207群 enet交流技术,主要是为了研究tcp内部执行机制.欢迎大家增加探讨.小弟水平有限,翻译难免有误. . http://enet.bespin.org 解析enet 双向链表 ...

  2. android码农神器 偷懒工具 android懒人框架 LoonAndroid 3 讲解

    LoonAndroid 3.0 Loonandroid是一个注解框架,不涉及任何UI效果,目的是一个功能一个方法,以方法为最小颗粒度对功能进行拆解.把功能傻瓜化,简单化,去掉重复性的代码,隐藏复杂的实 ...

  3. Android码农如何一个星期转为iOS码农(不忽悠)

    WeTest 导读 作为一个android客户端开发,如果你不懂点ios开发,怎么好意思说自己是客户端开发呢,本文讲解如何让android开发码农在一个星期上手IOS开发 --<记录自己IOS开 ...

  4. 拥抱Mac之码农篇

    拥抱Mac之码农篇 使用Mac大概两年时间.之前用着公司配的一台27寸的iMac.无奈机械硬盘严重拖慢速度,影响工作心情.于是入手Macbook Retina 13.这两年的开发工作所有在Mac上完毕 ...

  5. 【整理】待毕业.Net码农就业求职储备

    声明:本文题目来源于互联网,仅供即将从学校毕业的.Net码农(当然,我本人也是菜逼一个)学习之用.当然,学习了这些题目不一定会拿到offer,但是针对就业求职做些针对性的准备也是不错的.此外,除了技术 ...

  6. <开心一笑> 码农 黑客和2B程序员之间的区别

    笔记本电脑 码农: 黑客: 2B程序员: 求2的32次方: 码农: System.out.println(Math.pow(2, 32)); 黑客: System.out.println(1L< ...

  7. [2013 eoe移动开发者大会]靳岩:从码农到极客的升级之路

    (国内知名Android开发论坛 eoe开发者社区推荐:http://www.eoeandroid.com/) 前天,2013 eoe 移动开发者大会在国家会议中心召开,eoe 开发者社区创始人靳岩在 ...

  8. 《码农周刊》干货精选(Python 篇)

    <码农周刊>已经累计发送了 38 期,我们将干货内容进行了精选.此为 Python 篇. <码农周刊>往期回顾:http://weekly.manong.io/issues/ ...

  9. 一名Java架构师分享自己的从业心得,从码农到架构师我用了八年

    工作了挺久,发现有个挺有意思的现象,从程序员.高级程序员,到现在挂着架构师.专家之类的头衔,伴随着技术和能力的提高,想不明白的事情反而越来越多了. 这些疑问有些来自于跟小伙伴的交流,有些是我的自问自答 ...

随机推荐

  1. C++11 : 外部模板(Extern Template)

    在C++98/03语言标准中,对于源代码中出现的每一处模板实例化,编译器都需要去做实例化的工作:而在链接时,链接器还需要移除重复的实例化代码.显然,让编译器每次都去进行重复的实例化工作显然是不必要的, ...

  2. Java基础知识强化52:经典排序之冒泡排序(BubbleSort)

    1. 冒泡排序的原理图: 2. 冒泡排序代码实现: package cn.itcast_01; /* * 数组排序之冒泡排序: * 相邻元素两两比较,大的往后放,第一次完毕,最大值出现在了最大索引处 ...

  3. 关于解决方案和web文件夹放在同一目录路径错误的问题

    今天公司要做个b2b商城,下了个源码,目的是在这个基础上改,可是源码没有解决方案,于是建立了个解决方案,然后添加网站,发现解决方案和web目录位于不同目录(解决方案总是自动生成一个目录),可是我从网上 ...

  4. Request.ServerVariables 服务器环境变量

    Request.ServerVariables["Url"] 返回服务器地址 Request.ServerVariables["Path_Info"] 客户端提 ...

  5. for 迭代器遍历list map

    1.map与list区别     list是对象集合,允许对象重复. map是键值对的集合,不允许key重复 2.list 与 list<类型> list不限制类型,也就是object类型 ...

  6. MySql中查询表中的列名

    例如我的数据库名为"example",使用 USE example; 确定使用example数据库.使用 show tables; 显示数据库中的所有表.使用 DESC perso ...

  7. 在iis中调试asp.net程序

    第一步,在iis中新建一个网站,名称为Langben,“物理路径”选择你的程序的根目录,端口你可以随便设置一个数,我这里设置为8888(后面要用到哦). 第二步,应用程序池设置一下 第三步,接下来,在 ...

  8. mac版sublime text2包管理器安装步骤

    第一步: control+-打开命令执行窗口. 第二步: 将包管理器的代码复制到命令执行窗口: import urllib2,os,hashlib; h = '2915d1851351e5ee549c ...

  9. Python socket 广播信息到所有连接的客户端

    Python3,多线程,多客户端,广播数据 #!/usr/bin/env python3 import time import threading import queue import socket ...

  10. Java学习笔记--通过java.net.URLConnection发送HTTP请求

    http://www.cnblogs.com/nick-huang/p/3859353.html 使用Java API发送 get请求或post请求的步骤: 1. 通过统一资源定位器(java.net ...