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

时间: 2024-10-12 23:20:53

Android RakNet 系列之七 线程和服务端统计测试的相关文章

Android ListView分页加载(服务端+android端)Demo

Android ListView分页加载功能 在实际开发中经常用到,是每个开发者必须掌握的内容,本Demo给出了服务端+Android端的两者的代码,并成功通过了测试. 服务端使用MyEclipse,Android端使用Eclipse. 实现效果图: 服务端一共100条数据,共分四页,每页有25条数据. 源代码: 服务端: 需要导入图中这几个jar包. 在运行Android端代码前,需要开启服务端: 下面先给出服务端的代码: 类EmpDataSource: package com.android

[Android学习系列18]线程,进程,异步的一些事

解决NetworkOnMainThreadException http://www.aitinan.com/4387.html 参考: android进程与线程详解一:进程 android进程与线程详解二:线程 android进程与线程详解三:AsyncTask android进程与线程详解四:线程安全和进程间通信 [Android学习系列18]线程,进程,异步的一些事,码迷,mamicode.com

Android RakNet 系列之四 实现消息、语音、文件传输

简介 RakNet在Win平台上已经实现消息.语音.文件传输了,但在Android平台下尚未实现,笔者决定把源码移植到Android平台下测试. 详情 实现消息 项目自带Chat Example Client和Chat Example Server实现消息,源码简单易懂,此处就不介绍了,直接贴上图片. 测试通过,消息是以Toast方式显示的,图片未捕捉到显示. 实现语音 Win平台下实现语音是通过Portaudio进行的,Portaudio尚未支持Android,要实现语音怎么办? Java层实

服务端协议测试系列教程

测试技术分享之服务端协议测试系列教程 童鞋看完后有啥想法,可以发给我改进 在线播放地址:http://www.iqiyi.com/u/2013029540/a 下载地址:链接: http://pan.baidu.com/s/1boDHpbp 密码: p76e

Extjs GridPanel在有分页的情况下服务端统计合计实现方式

1.应用场景 在有分页的时候计算合计就不能采用Extjs gridpanel 提供的ftype: summary方式解决,否则只计算当前页的合计(因为数据只提取了当前页): 2.解决办法 重写Ext.grid.feature.Summary中的generateSummaryData函数 //方法一,临时重写 Ext.grid.feature.Summary.override({ generateSummaryData:function(){ return this.view.store.getP

Android RakNet 系列之二 功能介绍

简介 RakNet 已经成功地在Android平台上测试成功.RakNet的文档很多,实现起来很简单,下面对Raknet功能细节进行详细了解. 详情 1.RakNet使用哪些数据结构? 结构文件 描述 DS_BinarySearchTree.h 二叉搜索树,以及AVL平衡二叉搜索树 DS_BPlusTree.h B+树,用于快速查询,删除,和插入 DS_BytePool.h 返回某个大小门限的数据块,减少内存碎片 DS_ByteQueue.h 用于读写字节的队列 DS_Heap.h 堆数据结构体

Android RakNet 系列之六 源码说明

简介 既然选择Raknet开发,那就深入研究其源码结构,为以后的应用打下基础. 详情 1.文件 文件 描述 _FindFirst 快速查找 AutopatcherPatchContext 自动更新.不停 AutopatcherRepositoryInterface 更新 获取重要的数据接口 Base64Encoder base64编码 BitStream 比特流 流结构 CCRakNetSlidingWindow 观测 CCRakNetUDT   CheckSum 校验 CloudClient

Android RakNet 系列之一 项目介绍

简介 项目对通讯要求越来越高了,为了满足新的需求开始研究RakNet在Android平台的应用,本篇是第一篇,熟悉RakNet的各个功能和插件. RakNet是一个基于UDP网络传输协议的C++网络库,允许程序员在他们自己的程序中实现高效的网络传输服务.通常情况下用于游戏,但也可以用于其它项目. RakNet致力于网络和网络相关服务的游戏引擎.不仅包含了网络通信,也包括游戏级别复制,补丁升级,NAT穿透,和语音聊天.RakNet可以用于任何的应用,且可以与其他任何使用了RakNet的系统通信,不

Android Socket 聊天工具(一个服务端实现多个客户端间通信)

如果某位朋友也打算做这个Socket聊天工具,本人有个小小的建议,你可以不必太着急些代码,先想清楚自己最终要做到怎样效果,然后把自己的思路都写下来,有一个基本的实现方法.在写代码时就按照自己的思路一步一步地写下去,这样可以很好地避免写代码时由于思路不清左删右改. 以下是本人程序的设计思路 客户端设计思路: 一 用户登录界面 1 用一个EditText作为用户名输入口,用一个按键确定. 2 注册一个广播接收器,专门接收由后来的聊天界面发过来的消息广播(包括发信人,收信人,消息体). 3 创建一个客