📄 netudp.cc
字号:
/*///////////////////////////////////////////////////////////////////////////
UDP连接封装管理
李亦
2006.6.15
/*///////////////////////////////////////////////////////////////////////////
#include "platform/platform.h"
#include "platform/event.h"
#include "sim/netConnection.h"
#include "server/net/netUDP.h"
#include "core/bitStream.h"
#include "math/mRandom.h"
#include "platform/gameInterface.h"
//NetUDP *GNet = NULL;
namespace RPGServer
{
NetUDP::NetUDP(NetManager* pManager)
:Parent(pManager)
{
//AssertFatal(GNet == NULL, "ERROR: Multiple net interfaces declared.");
//GNet = this;
//m_pNetManager = pManager;
//mLastTimeoutCheckTime = 0;
//mAllowConnections = true;
}
void NetUDP::processPacketReceiveEvent(PacketReceiveEvent *prEvent)
{
if(prEvent->data[0] & 0x01)
{
Parent::processPacketReceiveEvent(prEvent);
return;
}
U32 dataSize = prEvent->size - PacketReceiveEventHeaderSize;
BitStream pStream(prEvent->data, dataSize);
// Otherwise, it's either a game info packet or a
// connection handshake packet.
U8 packetType;
pStream.read(&packetType);
NetAddress *addr = &prEvent->sourceAddress;
if(packetType <= GameHeartbeat)
handleInfoPacket(addr, packetType, &pStream);
else
{
// check if there's a connection already:
NetConnection *conn;
switch(packetType)
{
case ConnectChallengeRequest:
handleConnectChallengeRequest(addr, &pStream);
break;
case ConnectRequest:
handleConnectRequest(addr, &pStream);
break;
case ConnectChallengeResponse:
handleConnectChallengeResponse(addr, &pStream);
break;
case ConnectAccept:
handleConnectAccept(addr, &pStream);
break;
case Disconnect:
handleDisconnect(addr, &pStream);
break;
case ConnectReject:
handleConnectReject(addr, &pStream);
break;
}//switch
}//else
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Connection handshaking basic overview:
// The torque engine does a two phase connect handshake to
// prevent a spoofed source address Denial-of-Service (DOS) attack
//
// Basically, the initiator of a connection (client) sends a
// Connect Challenge Request packet to the server to initiate the connection
// The server then hashes the source address of the client request
// with some random magic server data to come up with a 16-byte key that
// the client can then use to gain entry to the server.
// This way there are no partially active connection records on the
// server at all.
//
// The client then sends a Connect Request packet to the server,
// including any game specific data necessary to start a connection (a
// server password, for instance), along with the key the server sent
// on the Connect Challenge Response packet.
//
// The server, on receipt of the Connect Request, compares the
// entry key with a computed key, makes sure it can create the requested
// NetConnection subclass, and then passes all processing on to the connection
// instance.
//
// If the subclass reads and accepts he connect request successfully, the
// server sends a Connect Accept packet - otherwise the connection
// is rejected with the sendConnectReject function
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void NetUDP::sendConnectChallengeRequest(NetConnection *conn)
{
Con::printf("Sending Connect challenge Request");
BitStream *out = BitStream::getPacketStream();
out->write(U8(ConnectChallengeRequest));
out->write(conn->getSequence());
conn->mConnectSendCount++;
conn->mConnectLastSendTime = Platform::getVirtualMilliseconds();
BitStream::sendPacketStream(conn->getNetAddress());
}
void NetUDP::handleConnectChallengeRequest(const NetAddress *addr, BitStream *stream)
{
char buf[256];
Net::addressToString(addr, buf);
Con::printf("Got Connect challenge Request from %s", buf);
if(!mAllowConnections)
return;
U32 connectSequence;
stream->read(&connectSequence);
if(!mRandomDataInitialized)
initRandomData();
U32 addressDigest[4];
computeNetMD5(addr, connectSequence, addressDigest);
BitStream *out = BitStream::getPacketStream();
out->write(U8(ConnectChallengeResponse));
out->write(connectSequence);
out->write(addressDigest[0]);
out->write(addressDigest[1]);
out->write(addressDigest[2]);
out->write(addressDigest[3]);
BitStream::sendPacketStream(addr);
}
//-----------------------------------------------------------------------------
void NetUDP::handleConnectChallengeResponse(const NetAddress *address, BitStream *stream)
{
Con::printf("Got Connect challenge Response");
U32 connectSequence;
stream->read(&connectSequence);
NetConnection *conn = findPendingConnection(address, connectSequence);
if(!conn || conn->getConnectionState() != NetConnection::AwaitingChallengeResponse)
return;
U32 addressDigest[4];
stream->read(&addressDigest[0]);
stream->read(&addressDigest[1]);
stream->read(&addressDigest[2]);
stream->read(&addressDigest[3]);
conn->setAddressDigest(addressDigest);
conn->setConnectionState(NetConnection::AwaitingConnectResponse);
conn->mConnectSendCount = 0;
Con::printf("Sending Connect Request");
sendConnectRequest(conn);
}
//-----------------------------------------------------------------------------
void NetUDP::sendConnectRequest(NetConnection *conn)
{
BitStream *out = BitStream::getPacketStream();
out->write(U8(ConnectRequest));
out->write(conn->getSequence());
U32 addressDigest[4];
conn->getAddressDigest(addressDigest);
out->write(addressDigest[0]);
out->write(addressDigest[1]);
out->write(addressDigest[2]);
out->write(addressDigest[3]);
out->writeString(conn->getClassName());
conn->writeConnectRequest(out);
conn->mConnectSendCount++;
conn->mConnectLastSendTime = Platform::getVirtualMilliseconds();
BitStream::sendPacketStream(conn->getNetAddress());
}
//-----------------------------------------------------------------------------
void NetUDP::handleConnectRequest(const NetAddress *address, BitStream *stream)
{
if(!mAllowConnections)
return;
Con::printf("Got Connect Request");
U32 connectSequence;
stream->read(&connectSequence);
// see if the connection is in the main connection table:
NetConnection *connect = NetConnection::lookup(address);
if(connect && connect->getSequence() == connectSequence)
{
sendConnectAccept(connect);
return;
}
U32 addressDigest[4];
U32 computedAddressDigest[4];
stream->read(&addressDigest[0]);
stream->read(&addressDigest[1]);
stream->read(&addressDigest[2]);
stream->read(&addressDigest[3]);
computeNetMD5(address, connectSequence, computedAddressDigest);
if(addressDigest[0] != computedAddressDigest[0] ||
addressDigest[1] != computedAddressDigest[1] ||
addressDigest[2] != computedAddressDigest[2] ||
addressDigest[3] != computedAddressDigest[3])
return; // bogus connection attempt
if(connect)
{
if(connect->getSequence() > connectSequence)
return; // the existing connection should be kept - the incoming request is stale.
else
connect->deleteObject(); // disconnect this one, and allow the new one to be created.
}
char connectionClass[255];
stream->readString(connectionClass);
ConsoleObject *co = ConsoleObject::create(connectionClass);
NetConnection *conn = dynamic_cast<NetConnection *>(co);
if(!conn || !conn->canRemoteCreate())
{
delete co;
return;
}
conn->registerObject();
conn->setNetAddress(address);
conn->setNetworkConnection(true);
conn->setSequence(connectSequence);
const char *errorString = NULL;
if(!conn->readConnectRequest(stream, &errorString))
{
sendConnectReject(conn, errorString);
conn->deleteObject();
return;
}
conn->setNetworkConnection(true);
conn->onConnectionEstablished(false);
conn->setEstablished();
conn->setConnectSequence(connectSequence);
sendConnectAccept(conn);
}
//-----------------------------------------------------------------------------
void NetUDP::sendConnectAccept(NetConnection *conn)
{
BitStream *out = BitStream::getPacketStream();
out->write(U8(ConnectAccept));
out->write(conn->getSequence());
conn->writeConnectAccept(out);
BitStream::sendPacketStream(conn->getNetAddress());
}
void NetUDP::handleConnectAccept(const NetAddress *address, BitStream *stream)
{
U32 connectSequence;
stream->read(&connectSequence);
NetConnection *conn = findPendingConnection(address, connectSequence);
if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse)
return;
const char *errorString = NULL;
if(!conn->readConnectAccept(stream, &errorString))
{
conn->handleStartupError(errorString);
removePendingConnection(conn);
conn->deleteObject();
return;
}
removePendingConnection(conn); // remove from the pending connection list
conn->setNetworkConnection(true);
conn->onConnectionEstablished(true); // notify the connection that it has been established
conn->setEstablished(); // installs the connection in the connection table, and causes pings/timeouts to happen
conn->setConnectSequence(connectSequence);
}
void NetUDP::sendConnectReject(NetConnection *conn, const char *reason)
{
if(!reason)
return; // if the stream is NULL, we reject silently
BitStream *out = BitStream::getPacketStream();
out->write(U8(ConnectReject));
out->write(conn->getSequence());
out->writeString(reason);
BitStream::sendPacketStream(conn->getNetAddress());
}
void NetUDP::handleConnectReject(const NetAddress *address, BitStream *stream)
{
U32 connectSequence;
stream->read(&connectSequence);
NetConnection *conn = findPendingConnection(address, connectSequence);
if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse &&
conn->getConnectionState() != NetConnection::AwaitingConnectResponse))
return;
removePendingConnection(conn);
char reason[256];
stream->readString(reason);
conn->onConnectionRejected(reason);
conn->deleteObject();
}
void NetUDP::handleDisconnect(const NetAddress *address, BitStream *stream)
{
NetConnection *conn = NetConnection::lookup(address);
if(!conn)
return;
U32 connectSequence;
char reason[256];
stream->read(&connectSequence);
stream->readString(reason);
if(conn->getSequence() != connectSequence)
return;
conn->onDisconnect(reason);
conn->deleteObject();
}
void NetUDP::handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream)
{
}
void NetUDP::startConnection(NetConnection *conn)
{
addPendingConnection(conn);
conn->mConnectionSendCount = 0;
conn->setConnectSequence(Platform::getVirtualMilliseconds());
conn->setConnectionState(NetConnection::AwaitingChallengeResponse);
// This is a the client side of the connection, so set the connection to
// server flag. We need to set this early so that if the connection times
// out, its onRemove() will handle the cleanup properly.
conn->setIsConnectionToServer();
// Everything set, so send off the request.
sendConnectChallengeRequest(conn);
}
void NetUDP::sendDisconnectPacket(NetConnection *conn, const char *reason)
{
Con::printf("Issuing Disconnect packet.");
// send a disconnect packet...
U32 connectSequence = conn->getSequence();
BitStream *out = BitStream::getPacketStream();
out->write(U8(Disconnect));
out->write(connectSequence);
out->writeString(reason);
BitStream::sendPacketStream(conn->getNetAddress());
}
void NetUDP::checkTimeouts()
{
Parent::checkTimeouts();
U32 time = Platform::getVirtualMilliseconds();
if(time < mLastTimeoutCheckTime + TimeoutCheckInterval)
return;
for(U32 i = 0; i < mPendingConnections.size();)
{
NetConnection *pending = mPendingConnections[i];
if(pending->getConnectionState() == NetConnection::AwaitingChallengeResponse &&
time > pending->mConnectLastSendTime + ChallengeRetryTime)
{
if(pending->mConnectSendCount > ChallengeRetryCount)
{
pending->onConnectTimedOut();
removePendingConnection(pending);
pending->deleteObject();
continue;
}
else
sendConnectChallengeRequest(pending);
}
else if(pending->getConnectionState() == NetConnection::AwaitingConnectResponse &&
time > pending->mConnectLastSendTime + ConnectRetryTime)
{
if(pending->mConnectSendCount > ConnectRetryCount)
{
pending->onConnectTimedOut();
removePendingConnection(pending);
pending->deleteObject();
continue;
}
else
sendConnectRequest(pending);
}
i++;
}
}
};//namespace RPGServer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -