標籤:
簡介
線程是程式中一個單一的順序控制流程程。進程內一個相對獨立的、可調度的執行單元,是系統獨立調度和指派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);#elsestatic 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;}
效果
統計詳情
類定義
// 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;}
效果
總結
本例測試了Raknet線程和統計,但要熟練掌握線程,還需要掌握如下知識:
線程棧模型與線程的變數、線程狀態的轉換、線程的同步與鎖、線程的互動、線程的調度-休眠 、線程的調度-優先順序、線程的調度-讓步、線程的調度-合并、線程的調度-守護線程、線程的同步-同步方法、線程的同步-同步塊 、並發協作-生產者消費者模型、並發協作-死結、volatile關鍵字、線程池、有傳回值的線程、鎖、訊號量、阻塞隊列、阻塞棧、條件變數、原子量、障礙器。
掌握了這些知識,你不牛逼也難了。
Android RakNet 系列之七 線程和服務端統計測試