|
发表于 2012-11-20 11:01:00
|
显示全部楼层
Re:MMOG网络同步算法揭秘(QQ幻想)
/*
Simple Network Library from "Networking for Game Programmers"
http://www.gaffer.org/networking-for-game-programmers
Author: Glenn Fiedler <gaffer@gaffer.org>
*/
#ifndef NET_H
#define NET_H
// platform detection
#define PLATFORM_WINDOWS 1
#define PLATFORM_MAC 2
#define PLATFORM_UNIX 3
#if defined(_WIN32)
#define PLATFORM PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define PLATFORM PLATFORM_MAC
#else
#define PLATFORM PLATFORM_UNIX
#endif
#if PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#pragma comment( lib, "wsock32.lib" )
#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#else
#error unknown platform!
#endif
#include <assert.h>
#include <vector>
#include <map>
#include <stack>
namespace net
{
// platform independent wait for n seconds
#if PLATFORM == PLATFORM_WINDOWS
void wait( float seconds )
{
Sleep( (int) ( seconds * 1000.0f ) );
}
#else
#include <unistd.h>
void wait( float seconds ) { usleep( (int) ( seconds * 1000000.0f ) ); }
#endif
#define MAX_PACKET_SIZE 512
// internet address
class Address
{
public:
Address()
{
address = 0;
port = 0;
}
Address( unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port )
{
this->address = ( a << 24 ) | ( b << 16 ) | ( c << 8 ) | d;
this->port = port;
}
Address( unsigned int address, unsigned short port )
{
this->address = address;
this->port = port;
}
unsigned int GetAddress() const
{
return address;
}
unsigned char GetA() const
{
return ( unsigned char ) ( address >> 24 );
}
unsigned char GetB() const
{
return ( unsigned char ) ( address >> 16 );
}
unsigned char GetC() const
{
return ( unsigned char ) ( address >> 8 );
}
unsigned char GetD() const
{
return ( unsigned char ) ( address );
}
unsigned short GetPort() const
{
return port;
}
bool operator == ( const Address & other ) const
{
return address == other.address && port == other.port;
}
bool operator != ( const Address & other ) const
{
return ! ( *this == other );
}
bool operator < ( const Address & other ) const
{
// note: this is so we can use address as a key in std::map
if ( address < other.address )
return true;
if ( address > other.address )
return false;
else
return port < other.port;
}
private:
unsigned int address;
unsigned short port;
};
// sockets
inline bool InitializeSockets()
{
#if PLATFORM == PLATFORM_WINDOWS
WSADATA WsaData;
return WSAStartup( MAKEWORD(2,2), &WsaData ) == NO_ERROR;
#else
return true;
#endif
}
inline void ShutdownSockets()
{
#if PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
}
class Socket
{
public:
Socket()
{
socket = 0;
}
~Socket()
{
Close();
}
bool Open( unsigned short port )
{
assert( !IsOpen() );
// create socket
socket = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( socket <= 0 )
{
printf( "failed to create socket\n" );
socket = 0;
return false;
}
// bind to port
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( (unsigned short) port );
if ( bind( socket, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
{
printf( "failed to bind socket\n" );
Close();
return false;
}
// set non-blocking io
#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
int nonBlocking = 1;
if ( fcntl( socket, F_SETFL, O_NONBLOCK, nonBlocking ) == -1 )
{
printf( "failed to set non-blocking socket\n" );
Close();
return false;
}
#elif PLATFORM == PLATFORM_WINDOWS
DWORD nonBlocking = 1;
if ( ioctlsocket( socket, FIONBIO, &nonBlocking ) != 0 )
{
printf( "failed to set non-blocking socket\n" );
Close();
return false;
}
#endif
return true;
}
void Close()
{
if ( socket != 0 )
{
#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
close( socket );
#elif PLATFORM == PLATFORM_WINDOWS
closesocket( socket );
#endif
socket = 0;
}
}
bool IsOpen() const
{
return socket != 0;
}
bool Send( const Address & destination, const void * data, int size )
{
assert( data );
assert( size > 0 );
if ( socket == 0 )
return false;
assert( destination.GetAddress() != 0 );
assert( destination.GetPort() != 0 );
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl( destination.GetAddress() );
address.sin_port = htons( (unsigned short) destination.GetPort() );
int sent_bytes = sendto( socket, (const char*)data, size, 0, (sockaddr*)&address, sizeof(sockaddr_in) );
return sent_bytes == size;
}
int Receive( Address & sender, void * data, int size )
{
assert( data );
assert( size > 0 );
if ( socket == 0 )
return false;
#if PLATFORM == PLATFORM_WINDOWS
typedef int socklen_t;
#endif
sockaddr_in from;
socklen_t fromLength = sizeof( from );
int received_bytes = recvfrom( socket, (char*)data, size, 0, (sockaddr*)&from, &fromLength );
if ( received_bytes <= 0 )
return 0;
unsigned int address = ntohl( from.sin_addr.s_addr );
unsigned short port = ntohs( from.sin_port );
sender = Address( address, port );
return received_bytes;
}
private:
int socket;
};
// connection
class Connection
{
public:
enum Mode
{
None,
Client,
Server
};
Connection( unsigned int protocolId, float timeout )
{
this->protocolId = protocolId;
this->timeout = timeout;
mode = None;
running = false;
ClearData();
}
~Connection()
{
if ( running )
Stop();
}
bool Start( int port )
{
assert( !running );
printf( "start connection on port %d\n", port );
if ( !socket.Open( port ) )
return false;
running = true;
return true;
}
void Stop()
{
assert( running );
printf( "stop connection\n" );
ClearData();
socket.Close();
running = false;
}
void Listen()
{
printf( "server listening for connection\n" );
ClearData();
mode = Server;
state = Listening;
}
void Connect( const Address & address )
{
printf( "client connecting to %d.%d.%d.%d:%d\n",
address.GetA(), address.GetB(), address.GetC(), address.GetD(), address.GetPort() );
ClearData();
mode = Client;
state = Connecting;
this->address = address;
}
bool IsConnecting() const
{
return state == Connecting;
}
bool ConnectFailed() const
{
return state == ConnectFail;
}
bool IsConnected() const
{
return state == Connected;
}
bool IsListening() const
{
return state == Listening;
}
Mode GetMode() const
{
return mode;
}
void Update( float deltaTime )
{
assert( running );
timeoutAccumulator += deltaTime;
if ( timeoutAccumulator > timeout )
{
if ( state == Connecting )
{
printf( "connect timed out\n" );
ClearData();
state = ConnectFail;
}
else if ( state == Connected )
{
printf( "connection timed out\n" );
ClearData();
if ( state == Connecting )
state = ConnectFail;
}
}
}
bool SendPacket( const unsigned char data[], int size )
{
assert( running );
if ( address.GetAddress() == 0 )
return false;
if (size > MAX_PACKET_SIZE)
{
return false;
}
unsigned char packet[MAX_PACKET_SIZE + 4];
//unsigned char packet[size+4];
packet[0] = (unsigned char) ( protocolId >> 24 );
packet[1] = (unsigned char) ( ( protocolId >> 16 ) & 0xFF );
packet[2] = (unsigned char) ( ( protocolId >> 8 ) & 0xFF );
packet[3] = (unsigned char) ( ( protocolId ) & 0xFF );
memcpy( &packet[4], data, size );
return socket.Send( address, packet, size + 4 );
}
int ReceivePacket( unsigned char data[], int size )
{
assert( running );
if (size > MAX_PACKET_SIZE)
{
return -1;
}
unsigned char packet[MAX_PACKET_SIZE + 4];
//unsigned char packet[size+4];
Address sender;
int bytes_read = socket.Receive( sender, packet, size + 4 );
if ( bytes_read == 0 )
return 0;
if ( bytes_read <= 4 )
return 0;
if ( packet[0] != (unsigned char) ( protocolId >> 24 ) ||
packet[1] != (unsigned char) ( ( protocolId >> 16 ) & 0xFF ) ||
packet[2] != (unsigned char) ( ( protocolId >> 8 ) & 0xFF ) ||
packet[3] != (unsigned char) ( protocolId & 0xFF ) )
return 0;
if ( mode == Server && !IsConnected() )
{
printf( "server accepts connection from client %d.%d.%d.%d:%d\n",
sender.GetA(), sender.GetB(), sender.GetC(), sender.GetD(), sender.GetPort() );
state = Connected;
address = sender;
}
if ( sender == address )
{
if ( mode == Client && state == Connecting )
{
printf( "client completes connection with server\n" );
state = Connected;
}
timeoutAccumulator = 0.0f;
memcpy( data, &packet[4], size - 4 );
return size - 4;
}
return 0;
}
protected:
void ClearData()
{
state = Disconnected;
timeoutAccumulator = 0.0f;
address = Address();
}
private:
enum State
{
Disconnected,
Listening,
Connecting,
ConnectFail,
Connected
};
unsigned int protocolId;
float timeout;
bool running;
Mode mode;
State state;
Socket socket;
float timeoutAccumulator;
Address address;
};
}
#endif
|
|