Android RakNet 系列之七 线程和服务端统计测试
简介
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
Raknet中重新封装了线程,类为:RakThread。
一个好的服务端体现在最大率使用内存,但并不是每一个程序员都可以把握好内存,一旦服务端出现了问题,我们查什么?日志。
日志就是服务端的黑匣子,统计了服务端的信息。
Raknet也提供了服务端连接统计。
线程详情
类定义
class RAK_DLL_EXPORT RakThread { public: /// Create a thread, simplified to be cross platform without all the extra junk /// To then start that thread, call RakCreateThread(functionName, arguments); /// \param[in] start_address Function you want to call /// \param[in] arglist Arguments to pass to the function /// \return 0=success. >0 = error code /* nice value Win32 Priority -20 to -16 THREAD_PRIORITY_HIGHEST -15 to -6 THREAD_PRIORITY_ABOVE_NORMAL -5 to +4 THREAD_PRIORITY_NORMAL +5 to +14 THREAD_PRIORITY_BELOW_NORMAL +15 to +19 THREAD_PRIORITY_LOWEST */ #if defined(_WIN32_WCE) static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist, int priority=0); #elif defined(_XBOX) || defined(X360) #elif defined(_WIN32) static int Create( unsigned __stdcall start_address( void* ), void *arglist, int priority=0); #else static int Create( void* start_address( void* ), void *arglist, int priority=0); #endif };
提供了多个平台
#if defined(_XBOX) || defined(X360) #elif defined(_WIN32) #include "WindowsIncludes.h" #include <stdio.h> #if !defined(_WIN32_WCE) #include <process.h> #endif #else #include <pthread.h> #endif
测试
for (i=0; i< 10; i++) { count[i]=i; RakNet::RakThread::Create(&ProducerThread, count+i); } for (; i < 20; i++) { count[i]=i; RakNet::RakThread::Create(&ConsumerThread, count+i ); }
RAK_THREAD_DECLARATION(ProducerThread) { char i = *((char *) arguments); char out[2]; out[0]=ID_USER_PACKET_ENUM; out[1]=i; while (endThreads==false) { printf("Thread %i writing...\n", i); if (i&1) peer1->Send(out, 2, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); else peer2->Send(out, 2, HIGH_PRIORITY, RELIABLE_ORDERED, 0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true); printf("Thread %i done writing\n", i); RakSleep(500); } return 0; } RAK_THREAD_DECLARATION(ConsumerThread) { char i = *((char *) arguments); RakNet::Packet *p; while (endThreads==false) { printf("Thread %i reading...\n", i); if (i&1) p=peer1->Receive(); else p=peer2->Receive(); printf("Thread %i done reading...\n", i); if (p) { if (p->data[0]==ID_USER_PACKET_ENUM) printf("Got data from thread %i\n", p->data[1]); if (i&1) peer1->DeallocatePacket(p); else peer2->DeallocatePacket(p); } RakSleep(500); } return 0; }
效果如图:
统计详情
类定义
// Connects, sends data over time, disconnects, repeat class Client { public: Client() { peer = RakNet::RakPeerInterface::GetInstance(); } ~Client() { RakNet::RakPeerInterface::DestroyInstance(peer); } void Startup(void) { RakNet::SocketDescriptor socketDescriptor;
{ peer->CloseConnection(peer->GetSystemAddressFromIndex(0),true,0); isConnected=false; } void Update(RakNet::TimeMS curTime) { Packet *p = peer->Receive(); while (p) { switch (p->data[0]) { case ID_CONNECTION_REQUEST_ACCEPTED: printf("ID_CONNECTION_REQUEST_ACCEPTED\n"); isConnected=true; break; // print out errors case ID_CONNECTION_ATTEMPT_FAILED: printf("Client Error: ID_CONNECTION_ATTEMPT_FAILED\n"); isConnected=false; break; case ID_ALREADY_CONNECTED: printf("Client Error: ID_ALREADY_CONNECTED\n"); break; case ID_CONNECTION_BANNED: printf("Client Error: ID_CONNECTION_BANNED\n"); break; case ID_INVALID_PASSWORD: printf("Client Error: ID_INVALID_PASSWORD\n"); break; case ID_INCOMPATIBLE_PROTOCOL_VERSION: printf("Client Error: ID_INCOMPATIBLE_PROTOCOL_VERSION\n"); break; case ID_NO_FREE_INCOMING_CONNECTIONS: printf("Client Error: ID_NO_FREE_INCOMING_CONNECTIONS\n"); isConnected=false; break; case ID_DISCONNECTION_NOTIFICATION: //printf("ID_DISCONNECTION_NOTIFICATION\n"); isConnected=false; break; case ID_CONNECTION_LOST: printf("Client Error: ID_CONNECTION_LOST\n"); isConnected=false; break; } peer->DeallocatePacket(p); p = peer->Receive(); } if (curTime>nextSendTime && isConnected) { peer->Send((const char*)&randomData,RANDOM_DATA_SIZE,HIGH_PRIORITY,RELIABLE_ORDERED,0,RakNet::UNASSIGNED_SYSTEM_ADDRESS,true); nextSendTime=curTime+30; } } bool isConnected; RakPeerInterface *peer; RakNet::TimeMS nextSendTime; };
// Just listens for ID_USER_PACKET_ENUM and validates its integrity class Server { public: Server() { peer = RakNet::RakPeerInterface::GetInstance(); } ~Server() { RakNet::RakPeerInterface::DestroyInstance(peer); } void Start(void) { RakNet::SocketDescriptor socketDescriptor; socketDescriptor.port=(unsigned short) SERVER_PORT; bool b = peer->Startup((unsigned short) NUM_CLIENTS,&socketDescriptor,1)==RakNet::RAKNET_STARTED; RakAssert(b); peer->SetMaximumIncomingConnections(NUM_CLIENTS); } unsigned ConnectionCount(void) const { unsigned i,count; for (i=0,count=0; i < NUM_CLIENTS;i++) if (peer->GetSystemAddressFromIndex(i)!=RakNet::UNASSIGNED_SYSTEM_ADDRESS) count++; return count; } void Update(RakNet::TimeMS curTime) { Packet *p = peer->Receive(); while (p) { switch (p->data[0]) { case ID_CONNECTION_LOST: case ID_DISCONNECTION_NOTIFICATION: case ID_NEW_INCOMING_CONNECTION: printf("Connections = %i\n", ConnectionCount()); break; case ID_USER_PACKET_ENUM: { if (memcmp(p->data, randomData, RANDOM_DATA_SIZE)!=0) { printf("Bad data on server!\n"); } break; } } peer->DeallocatePacket(p); p = peer->Receive(); } } RakPeerInterface *peer; };
测试
int main(void) { Client clients[NUM_CLIENTS]; Server server; // int clientIndex; int mode; printf("Connects many clients to a single server.\n"); printf("Difficulty: Intermediate\n\n"); printf("Run as (S)erver or (C)lient or (B)oth? "); char ch = getche(); static char *remoteIP="94.198.81.195"; static char *localIP="127.0.0.1"; if (ch==‘s‘ || ch==‘S‘) mode=0; else if (ch==‘c‘ || ch==‘c‘) { mode=1; remoteIPAddress=remoteIP; } else { mode=2; remoteIPAddress=localIP; } printf("\n"); unsigned i; randomData[0]=ID_USER_PACKET_ENUM; for (i=0; i < RANDOM_DATA_SIZE-1; i++) randomData[i+1]=i; if (mode==0 || mode==2) { server.Start(); printf("Started server\n"); } if (mode==1 || mode==2) { printf("Starting clients...\n"); for (i=0; i < NUM_CLIENTS; i++) clients[i].Startup(); printf("Started clients\n"); printf("Connecting clients...\n"); for (i=0; i < NUM_CLIENTS; i++) clients[i].Connect(); printf("Done.\n"); } RakNet::TimeMS endTime = RakNet::GetTimeMS()+60000*5; RakNet::TimeMS time = RakNet::GetTimeMS(); while (time < endTime) { if (mode==0 || mode==2) server.Update(time); if (mode==1 || mode==2) { for (i=0; i < NUM_CLIENTS; i++) clients[i].Update(time); } if (kbhit()) { char ch = getch(); if (ch==‘ ‘) { FILE *fp; char text[2048]; if (mode==0 || mode==2) { printf("Logging server statistics to ServerStats.txt\n"); fp=fopen("ServerStats.txt","wt"); for (i=0; i < NUM_CLIENTS; i++) { RakNetStatistics *rssSender; rssSender=server.peer->GetStatistics(server.peer->GetSystemAddressFromIndex(i)); StatisticsToString(rssSender, text, 3); fprintf(fp,"==== System %i ====\n", i+1); fprintf(fp,"%s\n\n", text); } fclose(fp); } if (mode==1 || mode==2) { printf("Logging client statistics to ClientStats.txt\n"); fp=fopen("ClientStats.txt","wt"); for (i=0; i < NUM_CLIENTS; i++) { RakNetStatistics *rssSender; rssSender=clients[i].peer->GetStatistics(clients[i].peer->GetSystemAddressFromIndex(0)); StatisticsToString(rssSender, text, 3); fprintf(fp,"==== Client %i ====\n", i+1); fprintf(fp,"%s\n\n", text); } fclose(fp); } } if (ch==‘q‘ || ch==0) break; } time = RakNet::GetTimeMS(); RakSleep(30); } if (mode==0 || mode==2) server.peer->Shutdown(0); if (mode==1 || mode==2) for (i=0; i < NUM_CLIENTS; i++) clients[i].peer->Shutdown(0); printf("Test completed"); return 0; }
效果如图:
总结
本例测试了Raknet线程和统计,但要熟练掌握线程,还需要掌握如下知识:线程栈模型与线程的变量、线程状态的转换、线程的同步与锁、线程的交互、线程的调度-休眠 、线程的调度-优先级、线程的调度-让步、线程的调度-合并、线程的调度-守护线程、线程的同步-同步方法、线程的同步-同步块 、并发协作-生产者消费者模型、并发协作-死锁、volatile关键字、线程池、有返回值的线程、锁、信号量、阻塞队列、阻塞栈、条件变量、原子量、障碍器。
掌握了这些知识,你不牛逼也难了。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。