- QQ 324186207群 enet交流技术,主要是为了研究tcp内部运行机制,欢迎大家加入探讨,小弟水平有限,翻译难免有误。。
http://enet.bespin.org - 解析enet 双向链表(无placement new) enet本身就已经局限了4095 在线人数 如果有10000人同时在线,enet使用list来维护每次收发,不断的销毁,释放内存,性能实在太低。 enent写的根本不严谨,不管什么结构都存储双向链表,收一个包,我也需要去遍历,到底获取某peer。
- enet_host_service 每次事件抛出机制,实在太粗糙,说明enet 更多偏向就是一个学习项目,如果大量传输数据,高并发,高负载,无法满足需求
- 顺便提下,为何服务器好多地方不使用stl
-
问题一:不能确定STL如何管理内存。假设就按照STL默认的方法来管理内容,则服务器在长时间的分配和释放内存后,容易导致内存碎片,对服务器的稳定有影响。然而,如果没有阅读过STL的源码,谁又能确切地得知STL是如何管理内存的呢?虽然可以在STL中使用自己的分配器,又能够确认STL的某些部分不会在分配器之外进行内存分配呢?
当然,内存管理问题不是STL带来的问题,任何服务器程序本身都要考虑这个问题。原因在极少有人去阅读STL的源码,去了解STL的内部实现机理。因为不了解,所以怀疑;因为怀疑,所以不轻易使用。
问题二:不能确定STL在海量数据下的表现。服务器一般都是海量的内存,为了提高性能大量数据保存在内存中。在非常大的规模的数据下,STL一定能够满足稳定性和效率的需求吗?
问题三:不能确定STL在多线程环境下的表现。以上的内存和规模的问题可以通过加深对STL的了解和测试来解决,但是多线程下的并发问题就不是那么容易解决了。STL不是线程安全的,在多线程环境下,对STL容器的操作都要加锁来确保正确。然后,部分高性能的场合,需要对“读-读”条件下并发进行优化,以及某些锁无关的特殊条件可以不加锁,甚至是采用流行的RCU机制…………在这个倡导多核和并发的时代,STL显得有些落后了。
- 传统的双向链表 一般情况我们套上 T *data
/*双链表结构*/- template<class T>
- typedef struct node
- {
- T *data;
- struct node *prior;
- struct node *next;
- }DNode;
- 以下enet双向链表结构,发现结构体内无T *data ,奇怪吧?那么我们数据存储到哪里?
- #list.h
- typedef struct _ENetListNode
- {
- struct _ENetListNode * next;
- struct _ENetListNode * previous;
- } ENetListNode;
- typedef ENetListNode * ENetListIterator;
- typedef struct _ENetList
- {
- ENetListNode sentinel; //标记当前处于某个节点位置
- } ENetList;
- extern void enet_list_clear( ENetList * );
- extern ENetListIterator enet_list_insert( ENetListIterator , void * );
- extern void * enet_list_remove( ENetListIterator );
- extern ENetListIterator enet_list_move( ENetListIterator, void *, void * );
- extern size_t enet_list_size( ENetList * );
- #define enet_list_begin(list) ((list) -> sentinel.next)
- #define enet_list_end(list) (& (list) -> sentinel)
- #define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list))
- #define enet_list_next(iterator) ((iterator) -> next)
- #define enet_list_previous(iterator) ((iterator) -> previous)
- #define enet_list_front(list) ((void *) (list) -> sentinel.next)
- #define enet_list_back(list) ((void *) (list) -> sentinel.previous)
- #main.cpp
- #define ENET_CALLBACK __cdecl
- typedef struct _ENetCallbacks
- {
- void * ( ENET_CALLBACK * malloc ) ( size_t size );
- void ( ENET_CALLBACK * free ) ( void * memory );
- void ( ENET_CALLBACK * no_memory ) ( void );
- } ENetCallbacks;
- static ENetCallbacks callbacks = { malloc, free, abort };
- void * enet_malloc( size_t size )
- {
- void * memory = callbacks.malloc( size );
- if( memory == NULL )
- callbacks.no_memory();
- return memory;
- }
- void enet_free( void * memory )
- {
- callbacks.free( memory );
- }
- struct ENetOutgoingCommand
- {
- ENetListNode list;
- int a;
- };
- int main()
- ENetOutgoingCommand *enet_outgoing;
- ENetList enet_test;
- enet_list_clear(&enet_test); //重置双向链表,将头指针和尾指针指向第一个空节点
- enet_outgoing = (ENetOutgoingCommand *)enet_malloc( sizeof( ENetOutgoingCommand ) );
- enet_outgoint->a = 100;
- enet_list_insert( enet_list_end( &enet_test ), enet_outgoing ); //malloc 数据插入到双向链表的尾部
- ENetListIterator currentCommand;
- currentCommand = enet_list_begin(&enet_test ); //这里取出双向链表头节点
- while( currentCommand != enet_list_end( &enet_test ) ) //遍历链表
- {
- ENetOutgoingCommand * t1 = (ENetOutgoingCommand *)currentCommand; //通过强转来获取数据
- cout << t1->a << endl;
- currentCommand = enet_list_next( currentCommand ); //获取下个节点
- enet_list_remove( &t1->list ); //删除当前节点
- enet_free( t1 );//删除内存,避免内存泄漏
- }
- while( Exit )
- {
- 开始服务器循环机制,这个循环设计非常的糟糕,每次一但服务器触发事件,会调用不同类型事件。
- 解决办法,每一个while 我用队列收一把所有数据,然后在逐个遍历,这样增加了很小延迟,但是提高了吞吐量。
- 每次收到数据后,又从新 enet_protocol_send_outgoing_commands 开始检查第一个玩家,应该做个全局变量,存储当前遍历 位置
- ENetEvent event;
- if( enet_host_service( server, &event, 1 ) )
- {
- switch( event.type )
- {
- case ENET_EVENT_TYPE_CONNECT:
- {
- Conneted( event.peer->connectID, event.peer->address.host, event.peer->address.port );
- peersConn.Insert( event.peer->connectID, event.peer );
- break;
- }
- case ENET_EVENT_TYPE_RECEIVE:
- {
- //bIdle = false;
- //Recived( event.peer->connectID, event.packet->data, event.packet->dataLength );
- break;
- }
- case ENET_EVENT_TYPE_DISCONNECT:
- {
- cout << "close client_fd" << endl;
- //Disconneted( event.connectID );
- break;
- }
- case ENET_EVENT_TYPE_PING:
- {
- cout << "enter reconnect:" << time( 0 ) << endl;
- //ReConnection(event.peer->connectID);
- break;
- }
- }
- }
- }
- /** Waits for events on the host specified and shuttles packets between
- the host and its peers.
- @param host host to service
- @param event an event structure where event details will be placed if one occurs
- if event == NULL then no events will be delivered
- @param timeout number of milliseconds that ENet should wait for events
- @retval > 0 if an event occurred within the specified time limit
- @retval 0 if no event occurred
- @retval < 0 on failure
- @remarks enet_host_service should be called fairly regularly for adequate performance
- @ingroup host
- */
- int
- enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout)
- {
- enet_uint32 waitCondition; //等待条件,用于socket设置,目前enet只有poll select 实现。
- if (event != NULL)
- {
- event ->type = ENET_EVENT_TYPE_NONE; //这里的event 就是每次我所需要收集的数据包结构
- event ->peer = NULL;
- event ->packet = NULL;
- event ->connectID = NULL;
- switch (enet_protocol_dispatch_incoming_commands (host, event)) //调度所有收包事件
- {
- case 1:
- return 1;
- case -1:
- #ifdef ENET_DEBUG
- perror ("Error dispatching incoming packets");
- #endif
- return -1;
- default:
- break;
- }
- }
- host ->serviceTime = enet_time_get (); //获取系统开始的毫秒时间
- timeout += host ->serviceTime;
- do
- {
- 流量控制,拥塞控制,有点看不懂,目前这块我还在学习中。
- if (ENET_TIME_DIFFERENCE (host ->serviceTime, host ->bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL)
- enet_host_bandwidth_throttle (host);
- 以下结构参考unix网络编程 select 非阻塞代码,结构,也就是说非阻塞模式下,必须写成这种结构。
- switch (enet_protocol_send_outgoing_commands (host, event, 1)) 所有数据包在这里发送
- {
- case 1:
- return 1;
- case -1:
- #ifdef ENET_DEBUG
- perror ("Error sending outgoing packets");
- #endif
- return -1;
- default:
- break;
- }
- 所有数据包,在这里接受
- switch (enet_protocol_receive_incoming_commands (host, event))
- {
- case 1:
- return 1;
- case -1:
- #ifdef ENET_DEBUG
- perror ("Error receiving incoming packets");
- #endif
- return -1;
- default:
- break;
- }
- switch (enet_protocol_send_outgoing_commands (host, event, 1))
- {
- case 1:
- return 1;
- case -1:
- #ifdef ENET_DEBUG
- perror ("Error sending outgoing packets");
- #endif
- return -1;
- default:
- break;
- }
- if (event != NULL)
- {
- switch (enet_protocol_dispatch_incoming_commands (host, event))
- {
- case 1:
- return 1;
- case -1:
- #ifdef ENET_DEBUG
- perror ("Error dispatching incoming packets");
- #endif
- return -1;
- default:
- break;
- }
- }
- if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
- return 0;
- do
- {
- host -> serviceTime = enet_time_get ();
- if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout))
- return 0;
- waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT;
- select poll 模型
- if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0)
- return -1;
- }
- while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT);
- host -> serviceTime = enet_time_get ();
- } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE);
- return 0;
- }
通向码农的道路(enet开源翻译计划 二)
最新推荐文章于 2022-04-03 17:07:07 发布