通向码农的道路(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, 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;

}

时间: 2024-10-25 11:56:30

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

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

http://enet.bespin.org 解析enet 双向链表(无placement new) enet本身就已经局限了4095 在线人数   如果有10000人同时在线,enet使用list来维护每次收发,不断的销毁,释放内存,性能实在太低. enent写的根本不严谨,不管什么结构都存储双向链表,收一个包,我也需要去遍历,到底获取某peer. enet_host_service  每次事件抛出机制,实在太粗糙,说明enet  更多偏向就是一个学习项目,如果大量传输数据,高并发,高负载,无

大量的文档,大量的示例代码,大量的开源组件,大量的社区,大量的码农

移动用各个平台的原生工具和代码,当年被Delphi忽悠,入了贼船,这次搞移动,坚定了跟着厂家走的策略.每次更新不用傻等Delphi跟进,大量的文档可以参考,大量的示例代码可以直接copy,大量的开源组件可以拿来就用,大量的社区可以做到有问必答. 如果有一天真的做大了,还有大量的iOS/Java码农可以招聘,组队团PK. 总之是选路要选对啊.这两年如果不是EMB出现救市,Delphi差点成了绝唱.想想都后怕.移动开发不敢在冒险了. 参考:http://bbs.2ccc.com/topic.asp?

Java架构师之路:从Java码农到年薪八十万的架构师,最牛Java架构师进阶路线

从Java码农到年薪八十万的架构师,资深架构师大牛给予Java技术提升学习路线建议,如何成为一名资深Java架构师? 对于工作多年的程序员而言,日后的职业发展无非是继续专精技术.转型管理和晋升架构师三种选择.架构师在一家公司有多重要.优秀架构师需要具备怎样的素质以及架构师的发展现状三个方面来分析 程序员如何才能晋升为优秀的高薪架构师? 希望通过本文让程序员们了解架构师的市场行情,了解架构师的发展前景,并帮助你更清晰地做出职业规划. 架构师在一家公司有多重要 架构师在公司中担当着「IT架构灵魂人物

老码农谈NDK开发

关于NDK,我也天真过 5.6年前刚拿到HTC的G1开始做Android开发时,得知Java可以和C混编激动不已,真的是拿到钥匙见什么都是锁,老想着用NDK做些事情.到后来公司做了一个带有强烈技术风格的业务决策,我被迫把NDK来回折腾了很长时间,也没能折腾出什么成果,但这个决策却把公司折腾完蛋,我也就对NDK渐渐心恢意冷了,真正体会到,做业务,服务用户为主,技术上,好用是王道. 今天收到一封邮件(来自:[email protected]),让我谈谈NDK,真的挠到了我的痒处,一直有些话是想跟对N

写给立志做码农的大学生(蘑菇街你都挂了,你还要面腾讯? 我去,我一定要去)

先简单介绍一下我自己,我是一所普通大学的本科生,大学录取时的专业是非计算机系的,在大一下学期意识到自己喜欢敲代码以后,就提交了转专业申请.大二起开始在计算机系学习.大三时(2015年4月)拿到了腾讯暑期实习的offer,暑期实习的过程中获得留用offer,大四没跑秋招,几乎就在学校浪荡了一年. 我不是大牛,不是来传播鸡汤或成功学的,只是最近有感于学弟学妹们在学习以及规划方面严重不足,觉得这是一个共性问题,遂捉起纸笔,写点东西. 1. 确定方向 1.1 选择比努力更重要 关于方向的选择其实越早确定

【知乎】怎么成为一个优秀的程序员,而不是一个优秀的码农?

怎么成为一个优秀的程序员,而不是一个优秀的码农? 9 条评论 分享 默认排序按时间排序 98 个回答 3844赞同反对,不会显示你的姓名 萧井陌 微信公众号:炼瓜研究所 技术社区 - 3844 人赞同 优秀的程序员会告诉你打根基的重要性,会劝你在厚积薄发前要隐忍. 优秀的码农会告诉你学啥底层.啥啥啥一拖就好了,学了python还要啥自行车啊,数据结构排序函数二分搜索这不都内置了吗?工作中永远用不到,学算法有啥用啊?成为高手有很多种方法汇编是个屁啊? +++基础的分割线+++ 列举几个我认为比较重

写给立志做码农的大学生

先简单介绍一下我自己,我是一所普通大学的本科生,大学录取时的专业是非计算机系的,在大一下学期意识到自己喜欢敲代码以后,就提交了转专业申请.大二起开始在计算机系学习.大三时(2015年4月)拿到了腾讯暑期实习的offer,暑期实习的过程中获得留用offer,大四没跑秋招,几乎就在学校浪荡了一年. 我不是大牛,不是来传播鸡汤或成功学的,只是最近有感于学弟学妹们在学习以及规划方面严重不足,觉得这是一个共性问题,遂捉起纸笔,写点东西. 1. 确定方向 1.1 选择比努力更重要 关于方向的选择其实越早确定

自动写代码工具要颠覆码农?(转)

摘要 : 人类总是会对自己的未来充满了焦虑,在我们对未来心存怀疑的时候,任何一则“消极”一点的消息都能让我们更加否认自己的未来,这一心理近日在对程序员前景心存质疑的人们身上,非常明显. 人类总是会对自己的未来充满了焦虑,在我们对未来心存怀疑的时候,任何一则“消极”一点的消息都能让我们更加否认自己的未来,这一心理近日在对程序员前景心存质疑的人们身上,非常明显. 日前,据网易科技报道:美国莱斯大学表示,作为五角大楼的疯狂科学部门,美国国防部先进研究计划署(DARPA)对代号为PLINY的自动填写编码

拥抱Mac之码农篇

拥抱Mac之码农篇 使用Mac大概两年时间,之前用着公司配的一台27寸的iMac,无奈机械硬盘严重拖慢速度,影响工作心情,于是入手Macbook Retina 13,这两年的开发工作全部在Mac上完成,也积累了一点心得,遂总结此文,文章主要介绍一些我认为可以提高程序员工作效率的工具软件,希望对使用Mac的码农有点帮助. 包管理 Mac系统上主要的包管理有Macport和Homebrew,类似于Debian系列的apt-get,Redhat的yum,主要用来安装一些开源软件,这些工具的存在大大简化