Android RakNet 系列之七 线程和服务端统计测试

时间:2022-10-07 18:08:48
简介

线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派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);elsepeer2->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();elsep=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);elsepeer2->DeallocatePacket(p);}        RakSleep(500);}return 0;}

效果如图:

Android RakNet 系列之七 线程和服务端统计测试

统计详情

类定义

// Connects, sends data over time, disconnects, repeatclass 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 errorscase 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 integrityclass 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;}

效果如图:

Android RakNet 系列之七 线程和服务端统计测试

Android RakNet 系列之七 线程和服务端统计测试


总结

本例测试了Raknet线程和统计,但要熟练掌握线程,还需要掌握如下知识:
线程栈模型与线程的变量、线程状态的转换、线程的同步与锁、线程的交互、线程的调度-休眠 、线程的调度-优先级、线程的调度-让步、线程的调度-合并、线程的调度-守护线程、线程的同步-同步方法、线程的同步-同步块 、并发协作-生产者消费者模型、并发协作-死锁、volatile关键字、线程池、有返回值的线程、锁、信号量、阻塞队列、阻塞栈、条件变量、原子量、障碍器。
掌握了这些知识,你不牛逼也难了。