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关键字、线程池、有返回值的线程、锁、信号量、阻塞队列、阻塞栈、条件变量、原子量、障碍器。
掌握了这些知识,你不牛逼也难了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。