简介
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派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关键字、线程池、有返回值的线程、锁、信号量、阻塞队列、阻塞栈、条件变量、原子量、障碍器。
掌握了这些知识,你不牛逼也难了。