开源游戏通讯引擎RakNet
Raknet是一个基于UDP网络传输协议的C++网络库,允许程序员在他们自己的程序中实现高效的网络传输服务。通常情况下用于游戏,但也可以用于其它项目。
Raknet有以下好处:
l 高性能 在同一台计算机上,Radnet可以实现在两个程序之间每秒传输25,000条信息;
l 容易使用 Raknet有在线用户手册,视频教程。每一个函数和类都有详细的讲解,每一个功能都有自己的例程;
l 跨平台,当前Raknet支持Windows, Linux, Macs,可以建立在Visual Studio, GCC, Code: Blocks, DevCPP 和其它平台上;
l 在线技术支持 RakNet有一个活跃的论坛,邮件列表,你只要给他们发信,他们可以在几小时之内回复你。
l 安全的传输 RakNet在你的代码中自动使用SHA1, AES128, SYN,用RSA避免传输受到攻击
l 音频传输 用Speex编码解码,8位的音频只需要每秒500字节传输。
l 远程终端 用RakNet,你能远程管理你的程序,包括程序的设置,密码的管理和日志的管理。
l 目录服务器 目录服务器允许服务器列举他们自己需要的客户端,并与他们连接。
l Autopatcher Autopatcher系统将限制客户端传输到服务端的文件,这样是为了避免一些不合法的用户将一些不合法的文件传输到服务端。
l 对象重载系统
l 网络数据压缩 BitStream类允许压缩矢量,矩阵,四元数和在-1到1之间的实数。
l 远程功能调用
l 强健的通信层 可以保障信息按照不同的信道传输
RakNet支持两种版权,如果你是做免费游戏,RakNet将是免费的。相反,你必须支付一定的费用。
去官方网站下载.
http://www.rakkarsoft.com
raknet入门例子 可以参考ChatExample
// ----------------------------------------------------------------------
// RakNet version 1.0
// Filename ChatExample.cpp
// Created by Rakkar Software (rakkar@jenkinssoftware.com) January 24, 2003
// Very basic chat engine example
// ----------------------------------------------------------------------
#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "RakSleep.h"
#include "PacketLogger.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#ifdef _WIN32
#include "Kbhit.h"
#endif
#include <stdio.h>
#include <string.h>
#if defined(_CONSOLE_2)
#include "Console2SampleIncludes.h"
#endif
// We copy this from Multiplayer.cpp to keep things all in one file for this example
unsigned char GetPacketIdentifier(Packet *p);
#ifdef _CONSOLE_2
_CONSOLE_2_SetSystemProcessParams
#endif
int main(void)
{
// Pointers to the interfaces of our server and client.
// Note we can easily have both in the same program
RakPeerInterface *server=RakNetworkFactory::GetRakPeerInterface();
RakNetStatistics *rss;
server->SetIncomingPassword("Rumpelstiltskin", (int)strlen("Rumpelstiltskin"));
server->SetTimeoutTime(30000,UNASSIGNED_SYSTEM_ADDRESS);
// PacketLogger packetLogger;
// server->AttachPlugin(&packetLogger);
// Holds packets
Packet* p;
// GetPacketIdentifier returns this
unsigned char packetIdentifier;
// Record the first client that connects to us so we can pass it to the ping function
SystemAddress clientID=UNASSIGNED_SYSTEM_ADDRESS;
// Holds user data
char portstring[30];
printf("This is a sample implementation of a text based chat server.\n");
printf("Connect to the project 'Chat Example Client'.\n");
printf("Difficulty: Beginner\n\n");
// A server
puts("Enter the server port to listen on");
gets(portstring);
if (portstring[0]==0)
strcpy(portstring, "1234");
puts("Starting server.");
// Starting the server is very simple. 2 players allowed.
// 0 means we don't care about a connectionValidationInteger, and false
// for low priority threads
SocketDescriptor socketDescriptor(atoi(portstring),0);
bool b = server->Startup(4, 30, &socketDescriptor, 1 );
server->SetMaximumIncomingConnections(4);
if (b)
puts("Server started, waiting for connections.");
else
{
puts("Server failed to start. Terminating.");
exit(1);
}
server->SetOccasionalPing(true);
server->SetUnreliableTimeout(1000);
DataStructures::List<RakNetSmartPtr<RakNetSocket> > sockets;
server->GetSockets(sockets);
printf("Ports used by RakNet:\n");
for (unsigned int i=0; i < sockets.Size(); i++)
{
printf("%i. %i\n", i+1, sockets[i]->boundAddress.port);
}
printf("My IP is %s\n", server->GetLocalIP(0));
printf("My GUID is %s\n", server->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString());
puts("'quit' to quit. 'stat' to show stats. 'ping' to ping.\n'ban' to ban an IP from connecting.\n'kick to kick the first connected player.\nType to talk.");
char message[2048];
// Loop for input
while (1)
{
// This sleep keeps RakNet responsive
RakSleep(30);
#ifdef _WIN32
if (kbhit())
{
// Notice what is not here: something to keep our network running. It's
// fine to block on gets or anything we want
// Because the network engine was painstakingly written using threads.
gets(message);
if (strcmp(message, "quit")==0)
{
puts("Quitting.");
break;
}
if (strcmp(message, "stat")==0)
{
rss=server->GetStatistics(server->GetSystemAddressFromIndex(0));
StatisticsToString(rss, message, 2);
printf("%s", message);
printf("Ping %i\n", server->GetAveragePing(server->GetSystemAddressFromIndex(0)));
continue;
}
if (strcmp(message, "ping")==0)
{
server->Ping(clientID);
continue;
}
if (strcmp(message, "kick")==0)
{
server->CloseConnection(clientID, true, 0);
continue;
}
if (strcmp(message, "getconnectionlist")==0)
{
SystemAddress systems[10];
unsigned short numConnections=10;
server->GetConnectionList((SystemAddress*) &systems, &numConnections);
for (int i=0; i < numConnections; i++)
{
printf("%i. %s\n", i+1, systems[i].ToString(true));
}
continue;
}
if (strcmp(message, "ban")==0)
{
printf("Enter IP to ban. You can use * as a wildcard\n");
gets(message);
server->AddToBanList(message);
printf("IP %s added to ban list.\n", message);
continue;
}
// Message now holds what we want to broadcast
char message2[2048];
// Append Server: to the message so clients know that it ORIGINATED from the server
// All messages to all clients come from the server either directly or by being
// relayed from other clients
message2[0]=0;
strcpy(message2, "Server: ");
strcat(message2, message);
// message2 is the data to send
// strlen(message2)+1 is to send the null terminator
// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
// RELIABLE_ORDERED means make sure the message arrives in the right order
// We arbitrarily pick 0 for the ordering stream
// UNASSIGNED_SYSTEM_ADDRESS means don't exclude anyone from the broadcast
// true means broadcast the message to everyone connected
server->Send(message2, (const int) strlen(message2)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
}
#endif
// Get a packet from either the server or the client
for (p=server->Receive(); p; server->DeallocatePacket(p), p=server->Receive())
{
// We got a packet, get the identifier with our handy function
packetIdentifier = GetPacketIdentifier(p);
// Check if this is a network message packet
switch (packetIdentifier)
{
case ID_DISCONNECTION_NOTIFICATION:
// Connection lost normally
printf("ID_DISCONNECTION_NOTIFICATION from %s\n", p->systemAddress.ToString(true));;
break;
case ID_NEW_INCOMING_CONNECTION:
// Somebody connected. We have their IP now
printf("ID_NEW_INCOMING_CONNECTION from %s with GUID %s\n", p->systemAddress.ToString(true), p->guid.ToString());
clientID=p->systemAddress; // Record the player ID of the client
break;
case ID_INCOMPATIBLE_PROTOCOL_VERSION:
printf("ID_INCOMPATIBLE_PROTOCOL_VERSION\n");
break;
case ID_MODIFIED_PACKET:
// Cheater!
printf("ID_MODIFIED_PACKET\n");
break;
case ID_CONNECTION_LOST:
// Couldn't deliver a reliable packet - i.e. the other system was abnormally
// terminated
printf("ID_CONNECTION_LOST from %s\n", p->systemAddress.ToString(true));;
break;
default:
// The server knows the static data of all clients, so we can prefix the message
// With the name data
printf("%s\n", p->data);
// Relay the message. We prefix the name for other clients. This demonstrates
// That messages can be changed on the server before being broadcast
// Sending is the same as before
sprintf(message, "%s", p->data);
server->Send(message, (const int) strlen(message)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, p->systemAddress, true);
break;
}
}
}
server->Shutdown(300);
// We're done with the network
RakNetworkFactory::DestroyRakPeerInterface(server);
return 0;
}
// Copied from Multiplayer.cpp
// If the first byte is ID_TIMESTAMP, then we want the 5th byte
// Otherwise we want the 1st byte
unsigned char GetPacketIdentifier(Packet *p)
{
if (p==0)
return 255;
if ((unsigned char)p->data[0] == ID_TIMESTAMP)
{
RakAssert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
}
else
return (unsigned char) p->data[0];
}
// ----------------------------------------------------------------------
// RakNet version 1.0
// Filename ChatExample.cpp
// Created by Rakkar Software (rakkar@jenkinssoftware.com) January 24, 2003
// Very basic chat engine example
// ----------------------------------------------------------------------
#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "PacketLogger.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#ifdef _WIN32
#include "Kbhit.h"
#include "WindowsIncludes.h" // Sleep
#else
#include "Kbhit.h"
#include <unistd.h> // usleep
#endif
// We copy this from Multiplayer.cpp to keep things all in one file for this example
unsigned char GetPacketIdentifier(Packet *p);
int main(void)
{
RakNetStatistics *rss;
// Pointers to the interfaces of our server and client.
// Note we can easily have both in the same program
RakPeerInterface *client=RakNetworkFactory::GetRakPeerInterface();
// client->InitializeSecurity(0,0,0,0);
//PacketLogger packetLogger;
//client->AttachPlugin(&packetLogger);
// Holds packets
Packet* p;
// GetPacketIdentifier returns this
unsigned char packetIdentifier;
// Just so we can remember where the packet came from
bool isServer;
// Record the first client that connects to us so we can pass it to the ping function
SystemAddress clientID=UNASSIGNED_SYSTEM_ADDRESS;
// Crude interface
// Holds user data
char ip[30], serverPort[30], clientPort[30];
// A client
isServer=false;
printf("This is a sample implementation of a text based chat client.\n");
printf("Connect to the project 'Chat Example Server'.\n");
printf("Difficulty: Beginner\n\n");
// Get our input
puts("Enter the client port to listen on");
gets(clientPort);
if (clientPort[0]==0)
strcpy(clientPort, "0");
puts("Enter IP to connect to");
gets(ip);
client->AllowConnectionResponseIPMigration(false);
if (ip[0]==0)
strcpy(ip, "127.0.0.1");
puts("Enter the port to connect to");
gets(serverPort);
if (serverPort[0]==0)
strcpy(serverPort, "1234");
// Connecting the client is very simple. 0 means we don't care about
// a connectionValidationInteger, and false for low priority threads
SocketDescriptor socketDescriptor(atoi(clientPort),0);
client->Startup(8,30,&socketDescriptor, 1);
client->SetOccasionalPing(true);
bool b = client->Connect(ip, atoi(serverPort), "Rumpelstiltskin", (int) strlen("Rumpelstiltskin"));
if (b)
puts("Attempting connection");
else
{
puts("Bad connection attempt. Terminating.");
exit(1);
}
printf("My IP is %s\n", client->GetLocalIP(0));
printf("My GUID is %s\n", client->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).ToString());
puts("'quit' to quit. 'stat' to show stats. 'ping' to ping.\n'disconnect' to disconnect. 'connect' to reconnnect. Type to talk.");
char message[2048];
// Loop for input
while (1)
{
// This sleep keeps RakNet responsive
#ifdef _WIN32
Sleep(30);
#else
usleep(30 * 1000);
#endif
if (kbhit())
{
// Notice what is not here: something to keep our network running. It's
// fine to block on gets or anything we want
// Because the network engine was painstakingly written using threads.
gets(message);
if (strcmp(message, "quit")==0)
{
puts("Quitting.");
break;
}
if (strcmp(message, "stat")==0)
{
rss=client->GetStatistics(client->GetSystemAddressFromIndex(0));
StatisticsToString(rss, message, 2);
printf("%s", message);
printf("Ping=%i\n", client->GetAveragePing(client->GetSystemAddressFromIndex(0)));
continue;
}
if (strcmp(message, "disconnect")==0)
{
printf("Enter index to disconnect: ");
char str[32];
gets(str);
if (str[0]==0)
strcpy(str,"0");
int index = atoi(str);
client->CloseConnection(client->GetSystemAddressFromIndex(index),false);
printf("Disconnecting.\n");
continue;
}
if (strcmp(message, "shutdown")==0)
{
client->Shutdown(100);
printf("Shutdown.\n");
continue;
}
if (strcmp(message, "startup")==0)
{
bool b = client->Startup(8,30,&socketDescriptor, 1);
if (b)
printf("Started.\n");
else
printf("Startup failed.\n");
continue;
}
if (strcmp(message, "connect")==0)
{
printf("Enter server port: ");
gets(serverPort);
if (serverPort[0]==0)
strcpy(serverPort, "1234");
bool b = client->Connect(ip, atoi(serverPort), "Rumpelstiltskin", (int) strlen("Rumpelstiltskin"));
if (b)
puts("Attempting connection");
else
{
puts("Bad connection attempt. Terminating.");
exit(1);
}
continue;
}
if (strcmp(message, "ping")==0)
{
if (client->GetSystemAddressFromIndex(0)!=UNASSIGNED_SYSTEM_ADDRESS)
client->Ping(client->GetSystemAddressFromIndex(0));
continue;
}
if (strcmp(message, "getlastping")==0)
{
if (client->GetSystemAddressFromIndex(0)!=UNASSIGNED_SYSTEM_ADDRESS)
printf("Last ping is %i\n", client->GetLastPing(client->GetSystemAddressFromIndex(0)));
continue;
}
// message is the data to send
// strlen(message)+1 is to send the null terminator
// HIGH_PRIORITY doesn't actually matter here because we don't use any other priority
// RELIABLE_ORDERED means make sure the message arrives in the right order
client->Send(message, (int) strlen(message)+1, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
}
// Get a packet from either the server or the client
for (p=client->Receive(); p; client->DeallocatePacket(p), p=client->Receive())
{
// We got a packet, get the identifier with our handy function
packetIdentifier = GetPacketIdentifier(p);
// Check if this is a network message packet
switch (packetIdentifier)
{
case ID_DISCONNECTION_NOTIFICATION:
// Connection lost normally
printf("ID_DISCONNECTION_NOTIFICATION\n");
break;
case ID_ALREADY_CONNECTED:
// Connection lost normally
printf("ID_ALREADY_CONNECTED\n");
break;
case ID_INCOMPATIBLE_PROTOCOL_VERSION:
printf("ID_INCOMPATIBLE_PROTOCOL_VERSION\n");
break;
case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want.
printf("ID_REMOTE_DISCONNECTION_NOTIFICATION\n");
break;
case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want.
printf("ID_REMOTE_CONNECTION_LOST\n");
break;
case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want.
printf("ID_REMOTE_NEW_INCOMING_CONNECTION\n");
break;
case ID_CONNECTION_BANNED: // Banned from this server
printf("We are banned from this server.\n");
break;
case ID_CONNECTION_ATTEMPT_FAILED:
printf("Connection attempt failed\n");
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
// Sorry, the server is full. I don't do anything here but
// A real app should tell the user
printf("ID_NO_FREE_INCOMING_CONNECTIONS\n");
break;
case ID_MODIFIED_PACKET:
// Cheater!
printf("ID_MODIFIED_PACKET\n");
break;
case ID_INVALID_PASSWORD:
printf("ID_INVALID_PASSWORD\n");
break;
case ID_CONNECTION_LOST:
// Couldn't deliver a reliable packet - i.e. the other system was abnormally
// terminated
printf("ID_CONNECTION_LOST\n");
break;
case ID_CONNECTION_REQUEST_ACCEPTED:
// This tells the client they have connected
printf("ID_CONNECTION_REQUEST_ACCEPTED to %s with GUID %s\n", p->systemAddress.ToString(true), p->guid.ToString());
printf("My external address is %s\n", client->GetExternalID(p->systemAddress).ToString(true));
break;
default:
// It's a client, so just show the message
printf("%s\n", p->data);
break;
}
}
}
// Be nice and let the server know we quit.
client->Shutdown(300);
// We're done with the network
RakNetworkFactory::DestroyRakPeerInterface(client);
return 0;
}
// Copied from Multiplayer.cpp
// If the first byte is ID_TIMESTAMP, then we want the 5th byte
// Otherwise we want the 1st byte
unsigned char GetPacketIdentifier(Packet *p)
{
if (p==0)
return 255;
if ((unsigned char)p->data[0] == ID_TIMESTAMP)
{
assert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
}
else
return (unsigned char) p->data[0];
}