Added ChatServer and soePlatform

This commit is contained in:
Anonymous
2014-01-17 03:40:51 -07:00
parent 26b88b4533
commit 4fae2dfe0f
183 changed files with 64549 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,726 @@
#ifndef UDPLIBRARY_UDPCONNECTION_H
#define UDPLIBRARY_UDPCONNECTION_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
namespace UdpLibrary
{
class UdpReliableChannel;
struct UdpConnectionStatistics
{
/////////////////////////////////////////////////////////////////////////
// these statistics are valid even if clock-sync is not used
// these statistics are never reset and should not be as the negotiated
// packetloss stats would get messed up if they were
// as such, use UdpConnection::ConnectionAge to determine how long they have been accumulating
/////////////////////////////////////////////////////////////////////////
udp_int64 totalBytesSent;
udp_int64 totalBytesReceived;
udp_int64 totalPacketsSent; // total packets we have sent
udp_int64 totalPacketsReceived; // total packets we have received
udp_int64 crcRejectedPackets; // total packets on our connection that have been rejected due to a crc error
udp_int64 orderRejectedPackets; // total packets on our connection that have been rejected due to an order error (only applicable for ordered channel)
udp_int64 duplicatePacketsReceived; // total reliable packets that we received where we had already received it before and threw it away
udp_int64 resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series
udp_int64 resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring
udp_int64 applicationPacketsSent;
udp_int64 applicationPacketsReceived;
udp_int64 iterations; // number of times this connection has been given processing time
udp_int64 corruptPacketErrors; // number of misformed/corrupt packets
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// these statistics are only valid if clock-sync'ing is enabled (highly recommended) (will be valid on both client and server side)
// these statistics are reset by PingStatReset and are negotiated periodically by the clock-sync stuff (Params::clockSyncDelay)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int masterPingAge; // only valid (and applicable) on client side
int masterPingTime;
int averagePingTime;
int lowPingTime;
int highPingTime;
int lastPingTime;
int reliableAveragePing; // the average time (over last 3 acks) for a reliable packet to get acked (when packet is not lost)
udp_int64 syncOurSent; // total packets we have sent at time they reported their numbers
udp_int64 syncOurReceived; // total packets we have received at time they reported their numbers
udp_int64 syncTheirSent; // total packets they have sent
udp_int64 syncTheirReceived; // total packets they have received
float percentSentSuccess;
float percentReceivedSuccess;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The purpose of the UdpConnection is to manage a single logical connection
////////////////////////////////////////////////////////////////////////////////////////////////////////////
class UdpConnection : public UdpGuardedRefCount, public PriorityQueueMember, public HashTableMember1<UdpConnection>, public HashTableMember2<UdpConnection>
{
public:
enum Status { cStatusNegotiating, cStatusConnected, cStatusDisconnected, cStatusDisconnectPending, cStatusCount };
enum DisconnectReason { cDisconnectReasonNone, cDisconnectReasonIcmpError, cDisconnectReasonTimeout
, cDisconnectReasonOtherSideTerminated, cDisconnectReasonManagerDeleted
, cDisconnectReasonConnectFail, cDisconnectReasonApplication
, cDisconnectReasonUnreachableConnection, cDisconnectReasonUnacknowledgedTimeout
, cDisconnectReasonNewConnectionAttempt, cDisconnectReasonConnectionRefused
, cDisconnectReasonMutualConnectError, cDisconnectReasonConnectingToSelf
, cDisconnectReasonReliableOverflow, cDisconnectReasonApplicationReleased
, cDisconnectReasonCorruptPacket
, cDisconnectReasonCount };
// returns the current status of this connection
Status GetStatus() const;
// returns the reason that a connection was disconnected. See the enum above for a list of all the reasons
DisconnectReason GetDisconnectReason() const;
char *GetDisconnectReasonText(char *buf, int bufLen) const;
DisconnectReason GetOtherSideDisconnectReason() const;
static const char *DisconnectReasonText(DisconnectReason reason); // text-description of disconnect reason to aid in logging
// sets the handler object for this connection. If a handler object is specified, then the callback functions specified
// in UdpManager::Params are ignored for this connection and the handler is used for the callback instead.
// by default there is no handler.
void SetHandler(UdpConnectionHandler *handler);
UdpConnectionHandler *GetHandler() const;
// set and get the pass-through data value. Typically the application will set the pass through data
// in the callback function for establishing a connection, then it will use the pass through data
// in the callback function for routing packets.
void SetPassThroughData(void *passThroughData);
void *GetPassThroughData() const;
// when called this connection is marked as terminated. It is the responsibility of the application
// to explicitly destroy connections that are no longer connected. When this object is disconnected
// it calls the UdpManager and has itself removed from the list of active connections, at which point
// the only person having a pointer to this object is the application itself (which owns it)
// (if the UdpManager is deleted before all UdpConnections are destroyed, the UdpManager loops through
// all of the connections it has calling Disconnect on them such that they know that they no longer
// have a udp manager that they can send data through)
//
// setting a flushTimeout tells the connection to stay alive for that amount of time trying to send any pending
// reliable data before shutting down. Once the application calls Disconnect even with a flushTimeout, the application
// should not attempt to use the connection in any significant way (see docs and release notes for details)
// note: the notifyApplication parameter was removed from this function and the functionality of the library
// was changed such that ANYTIME the connection objects state changes to cStatusDisconnected, the OnTerminated callback
// function gets called.
void Disconnect(int flushTimeout = 0);
// sends a logical packet on the specified channel, returns FALSE if packet could not be queued for sending (should never happen)
// Internally, a packet that starts with a 0 byte is considered an internal control packet. If a logical packet starts with a 0
// byte, then there will an extra control byte of overhead in order to facilitate it. It is recommended that if packet size
// is critical, that you don't start the packet with a 0 byte. Typically an application will have a packet-type byte on the front
// of application packets; the application packet types should simply start at 1.
bool Send(UdpChannel channel, const void *data, int dataLen);
// same as the regular Send only it takes a LogicalPacket instead. There are two huge advantages to having it take a
// LogicalPacket. First, we can send the same LogicalPacket to multiple locations and each connection will not necessarily
// have to make its own copy of the data at the time it is put into the send queue (instead each connection just increments
// the buffer ref-count). Second, it allows the application to pre-generate very large packets (like file update packets potentially)
// and hold onto them for the entire length of the application, then, whenever any player needs that chunk of data, it can send them
// the already formatted LogicalPacket.
bool Send(UdpChannel channel, const LogicalPacket *packet);
// manually forces all channels to send-off any data they have queued up waiting for processing time to send
// this mainly applies to reliable channels. When you send a reliable packet, it actually only adds it to the reliable
// queue until the connection is given processing time by the manager object. This call forces it to attempt to
// send that queued data immediately (subject to normal flow control restrictions). This also flushes the multi-buffer
// for the channel. If you send reliable data and want to ensure that it goes out immediately after the send, this is the
// best call to make.
void FlushChannels();
// manually forces buffered data to be sent immediately
void FlushMultiBuffer();
// returns the number of bytes sent/received in the last second to this connection (accurate to within cBinResolution(25) milliseconds)
// these functions are not const as they expire the older bin data internally in order to calculate the number
int OutgoingBytesLastSecond();
int IncomingBytesLastSecond();
// returns the total number of bytes outstanding in all reliable channels. When this is zero, you know for sure
// that all sent reliable data has arrived at destination and is confirmed.
int TotalPendingBytes() const;
// returns how long has elapsed since this connection received data (in milliseconds)
int LastReceive() const;
// returns how long has elapsed since this connection received data (in milliseconds), using useStamp as the current time (optimization)
int LastReceive(UdpClockStamp useStamp) const;
// returns how long has elapsed since this connection sent data (in milliseconds)
int LastSend() const;
// returns how long this connection has been in existence (in milliseconds)
int ConnectionAge() const;
// returns the UdpManager object that is managing this connection
// will return NULL if the connection has been disconnected for some reason (because disconnecting severes the link to UdpManager)
UdpManager *GetUdpManager() const;
// returns the 32-bit encryption-code that was negotiated as part of the connection-establishment process.
// this is a randomly generated number that both the client and the server have in common. It is exposed
// via this interface primarily to allow user-supplied encrypt routines access to it.
// this code is generated by the server side in response to a connect request.
int GetEncryptCode() const;
// this returns the connection-code. This is very similar to the encrypt-code in that it is randomly
// generated and both ends of the connection will report the same value. The difference is that this
// code's purpose is part of the internal protocol to ensure that old connections don't try to process
// new connection request packets. Unlike the encrypt-code, this value is generated by the client
// and is part of the connect-request packet. Nevertheless, since this number will be the same random
// number on both ends of the connection, it too can be used as a potential encryption key for the user
// supplied encrypt routines. It's not quite as secure as the encrypt code since this value in theory
// could be hacked to be something predictable on the client side.
int GetConnectCode() const;
// returns a sync-stamp that can be compared to other ServerSyncStamp's generated on other machines
// in order to calculate the one-way travel time for a packet. It can only accurate calculate
// packet travel times under 32 seconds, would should be completely safe. You must use the
// UdpManager::SyncStampDeltaTime function in order to calculate the elapsed time between
// the two stamps.
udp_ushort ServerSyncStampShort() const;
udp_uint ServerSyncStampLong() const;
int ServerSyncStampShortElapsed(udp_ushort syncStamp) const;
int ServerSyncStampLongElapsed(udp_uint syncStamp) const;
// returns the IP address/port this connection is linked to
UdpPlatformAddress GetDestinationIp() const;
int GetDestinationPort() const;
char *GetDestinationString(char *buf, int bufLen) const;
// statistical functions
void GetStats(UdpConnectionStatistics *cs);
void PingStatReset(); // resets the ping-stat information, causing it to resync the clock etc (if in clock-sync mode). Generally this is not done, it was added for backward compatibility
// functions for manipulating the automatic no-data-disconnect stuff on a per-connection basis
void SetNoDataTimeout(int noDataTimeout); // 0=never timeout, otherwise in milliseconds (overrides UdpManager::Params::noDataTimeout setting, which is the default)
int GetNoDataTimeout() const;
// functions for manipulating the keep-alive packet sending on a per-connection basis
void SetKeepAliveDelay(int keepAliveDelay);
int GetKeepAliveDelay() const;
// configures whether this connection is in silent-disconnect mode or not. By default, the connection is not in silent
// disconnect mode, which means that when this connection is terminated, it will send a final terminate-packet to the
// other side telling them that we are disconnected, allowing them to quickly realize that the connection is now dead.
// In some circumstances, it may be desireable to not do this, and this can be accomplished by calling this function
// passing in 'true' to put it in silent mode. This may be desireable in cases where you are disconnecting a cheater
// and don't want them to have immediate notification that they did something bad. Or, if you are attempting to test
// timeout functionality on the other end and want to simulate a truly dead connection. Normally, you will not want
// to mess with this. It was added to the API to support some internal functionality, see its use in the source-code
// or release-notes for details.
void SetSilentDisconnect(bool silent);
// returns the current queue-status of the reliable channel specified. Unreliable channels will always report zero.
struct ChannelStatus
{
int totalPendingBytes; // total bytes of data in channel that have yet to be acknowledged (includes queuedBytes plus physical-packet bytes that have yet to be acknowledged)
int queuedPackets; // number of logical packets in the queue
int queuedBytes; // number of bytes in the logical queue (the logical queue does NOT include pending physical packets)
int incomingLargeTotal; // total number of bytes in the currently incoming logical packet (only meaningful obviously if a fragmented file is in tranist)
int incomingLargeSoFar; // number of bytes received so far in the currently incoming logical packet
int oldestUnacknowledgedAge; // age of the oldest unacknowledged (but sent) packet (in milliseconds)
int duplicatePacketsReceived; // number of times we received a packet that we had already received
int resentPacketsAccelerated; // number of times we have resent a packet due to receiving a later packet in the series
int resentPacketsTimedOut; // number of times we have resent a packet due to the ack-timeout expiring
int congestionSlowStartThreshhold; // current threshhold for slow-start algorithm
int congestionWindowSize; // current sliding window size
int ackAveragePing; // average time for a packet to be acknowledged (used in calculating optimal resend timeouts)
};
void GetChannelStatus(UdpChannel channel, ChannelStatus *channelStatus) const;
protected:
friend class UdpManager;
friend class UdpReliableChannel;
// note: if connectPacket is NULL, that means this connection object is being created to establish
// a new connection to the specified ip/port (ie. the connection starts out in cStatusNegotiating mode)
// if connectPacket is non-NULL, that menas this connection object is being created to handle an
// incoming connect request and it will start out in cStatusConnected mode.
UdpConnection(UdpManager *udpManager, UdpPlatformAddress destIp, int destPort, int timeout); // starts connection-establishment protocol
UdpConnection(UdpManager *udpManager, const UdpManager::PacketHistoryEntry *e); // starts already connected, replying to connection request
// gives this connection processing time (only given processing time by the manager object and then
// only when the connection has scheduled itself to receive processing time)
void GiveTime(bool fromManager);
void ProcessRawPacket(const UdpManager::PacketHistoryEntry *e);
void PortUnreachable();
void FlagPortUnreachable();
// these functions are called by the manager to forward these events to this connection
// all events get sent to the UdpManager for potential event-queuing, then forwarded back
// to the connection for actual deliver, since the connection object needs to hold a
// guard such that the handler doesn't get deleted during event delivery
void OnRoutePacket(const udp_uchar *data, int dataLen);
void OnConnectComplete();
void OnTerminated();
void OnCrcReject(const udp_uchar *data, int dataLen);
void OnPacketCorrupt(const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
protected:
typedef int (UdpConnection::* IEncryptFunction)(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
typedef int (UdpConnection::* IDecryptFunction)(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
IDecryptFunction mDecryptFunction[cEncryptPasses];
IEncryptFunction mEncryptFunction[cEncryptPasses];
UdpPlatformAddress mIp;
int mPort;
int mSimulateOutgoingQueueBytes; // used by UdpManager to track how many bytes are in it's simulation queue headed to each destination
private:
~UdpConnection();
void Init(UdpManager *udpManager, UdpPlatformAddress destIp, int destPort);
// note: BufferedSend is capable of optionally taking two chunks of data at once, which are then concatenated together as if they were one chunk of data
// into the multi-buffer. Providing this facility prevents the UdpReliableChannel object from having to make a copy of all the data it sends
// in order to stick a realiable header on it.
// we don't bother extending this down to the PhysicalSend (in case the BufferedSend does a pass through due to size) because the encryption code
// is incapable of sourcing from two different chunks and outputting to one chunk. It's not possible to change that either, since the encyption
// takes place 32 bits at a time and you could end up straddling boundaries between chunks.
void RawSend(const udp_uchar *data, int dataLen); // nothing happens to the data here, it is given to the udpmanager and sent out the port
void PhysicalSend(const udp_uchar *data, int dataLen, bool appendAllowed); // sends a physical packet (encrypts and adds crc bytes)
udp_uchar *BufferedSend(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2, bool appendAllowed); // buffers logical packets waiting til we have more data (makes multi-packets)
bool InternalSend(UdpChannel channel, const udp_uchar *data, int dataLen, const udp_uchar *data2 = NULL, int dataLen2 = 0);
void InternalGiveTime();
void InternalDisconnect(int flushTimeout, DisconnectReason reason);
void ProcessCookedPacket(const udp_uchar *data, int dataLen);
void DecryptIt(const udp_uchar *data, int dataLen);
void ScheduleTimeNow();
void ExpireSendBin();
void ExpireReceiveBin();
void SendTerminatePacket(int connectCode, DisconnectReason reason);
void CallbackRoutePacket(const udp_uchar *data, int dataLen);
void CallbackCorruptPacket(const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
bool IsNonEncryptPacket(const udp_uchar *data) const;
// these encrypt-method functions return the length of the encrypted/decrypted data
// new methods of encryption/compression can be easily added by simply creating the
// functions for them and changing the SetupEncryptModel function as appropriate
// since raw packets are encrypted in the first place and have a limited size
// the decrypted data will never be larger than a maxRawPacketSize. Both of encrypt
// and decrypt are guaranteed to have enough room in dest buffers to hold the results.
// Encryption function is allowed to expand the data at most the number of bytes
// it reserves for this purpose in the SetupEncryptModel function.
int EncryptNone(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int DecryptNone(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int EncryptXor(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int DecryptXor(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int EncryptXorBuffer(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int DecryptXorBuffer(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int EncryptUserSupplied(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int DecryptUserSupplied(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int EncryptUserSupplied2(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
int DecryptUserSupplied2(udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
void SetupEncryptModel();
UdpLinkedListMember<UdpConnection> mConnectionLink;
UdpLinkedListMember<UdpConnection> mDisconnectPendingLink;
Status mStatus;
void *mPassThroughData;
UdpManager *mUdpManager;
int mConnectCode;
UdpConnectionStatistics mConnectionStats;
UdpClockStamp mConnectionCreateTime;
int mConnectAttemptTimeout;
int mNoDataTimeout;
DisconnectReason mDisconnectReason;
DisconnectReason mOtherSideDisconnectReason;
bool mFlaggedPortUnreachable;
bool mSilentDisconnect;
UdpReliableChannel *mChannel[cReliableChannelCount];
struct Configuration
{
int encryptCode;
int crcBytes;
EncryptMethod encryptMethod[cEncryptPasses];
int maxRawPacketSize; // negotiated maxRawPacketSize (ie. smaller of what two sides are set to)
};
Configuration mConnectionConfig;
int mOtherSideProtocolVersion;
UdpClockStamp mLastClockSyncTime;
UdpClockStamp mDataHoldTime;
UdpClockStamp mLastSendTime;
UdpClockStamp mLastReceiveTime;
UdpClockStamp mLastPortAliveTime;
udp_uchar *mMultiBufferData;
udp_uchar *mMultiBufferPtr;
int mOrderedCountOutgoing;
int mOrderedCountOutgoing2;
udp_ushort mOrderedStampLast;
udp_ushort mOrderedStampLast2;
udp_uchar *mEncryptXorBuffer;
int mEncryptExpansionBytes;
udp_uint mSyncTimeDelta;
int mSyncStatTotal;
int mSyncStatCount;
int mSyncStatLow;
int mSyncStatHigh;
int mSyncStatLast;
int mSyncStatMasterRoundTime;
UdpClockStamp mSyncStatMasterFixupTime;
bool mGettingTime;
UdpConnectionHandler *mHandler;
int mKeepAliveDelay;
UdpClockStamp mIcmpErrorRetryStartStamp;
UdpClockStamp mPortRemapRequestStartStamp;
UdpClockStamp mDisconnectFlushStamp;
int mDisconnectFlushTimeout;
mutable UdpPlatformGuardObject mGuard;
mutable UdpPlatformGuardObject mHandlerGuard;
// data rate management functions
enum { cBinResolution = 25, cBinCount = 1000 / cBinResolution };
udp_int64 mLastSendBin;
udp_int64 mLastReceiveBin;
int mOutgoingBytesLastSecond;
int mIncomingBytesLastSecond;
int mSendBin[cBinCount];
int mReceiveBin[cBinCount];
//////////////////////////////////////////////////////////////////////////////////////////////////////
// The following structs represent what the internal packets look like. In practice, most of these
// structs are never used and exist only for documentation clarity. Internally packets are
// manually assembled such that struct packing and byte-ordering issues won't be an issue.
//////////////////////////////////////////////////////////////////////////////////////////////////////
struct UdpPacketConnect
{
udp_uchar zeroByte;
udp_uchar packetType;
int protocolVersion;
int connectCode;
int maxRawPacketSize;
};
struct UdpPacketConfirm
{
udp_uchar zeroByte;
udp_uchar packetType;
int connectCode;
Configuration config;
int maxRawPacketSize;
};
struct UdpPacketTerminate
{
udp_uchar zeroByte;
udp_uchar packetType;
int connectCode;
};
struct UdpPacketKeepAlive
{
udp_uchar zeroByte;
udp_uchar packetType;
};
struct UdpPacketGroup
{
// this format is prepped by the GroupLogicalPacket object, which reports itself to the UdpConnection object as an internal packet
// type such that it doesn't get treated as an application-packet even though the application is the one sending it
udp_uchar zeroByte;
udp_uchar packetType;
// variableValue/data, repeated...
};
struct UdpPacketClockSync
{
udp_uchar zeroByte;
udp_uchar packetType;
udp_ushort timeStamp;
int masterPingTime;
int averagePingTime;
int lowPingTime;
int highPingTime;
int lastPingTime;
udp_int64 ourSent;
udp_int64 ourReceived;
};
struct UdpPacketClockReflect
{
udp_uchar zeroByte;
udp_uchar packetType;
udp_ushort timeStamp;
udp_uint serverSyncStampLong;
udp_int64 yourSent;
udp_int64 yourReceived;
udp_int64 ourSent;
udp_int64 ourReceived;
};
struct UdpPacketReliable
{
udp_uchar zeroByte;
udp_uchar packetType;
udp_ushort reliableStamp;
};
struct UdpPacketReliableFragmentStart
{
UdpPacketReliable reliable;
int length;
};
struct UdpPacketAck
{
udp_uchar zeroByte;
udp_uchar packetType;
udp_ushort reliableStamp;
};
struct UdpPacketOrdered
{
udp_uchar zeroByte;
udp_uchar packetType;
udp_ushort orderStamp;
};
enum { cUdpPacketReliableSize = 4 };
enum { cUdpPacketOrderedSize = 4 };
protected:
friend class GroupLogicalPacket; // so it can see cUdpPacketGroup enum
// note: cUdpPacketReliable, cUdpPacketFragment both indicate a reliable-packet header. They are marked
// differently such that we can support large packets without any additional header overhead, a fragment marked packet means
// that the packet is part of a larger packet being assembled. The first fragment has an additional 4 bytes on the header specifying
// the length to follow. The order of those entries is important
enum UdpPacketType { cUdpPacketZeroEscape, cUdpPacketConnect, cUdpPacketConfirm, cUdpPacketMulti, cUdpPacketBig
, cUdpPacketTerminate, cUdpPacketKeepAlive
, cUdpPacketClockSync, cUdpPacketClockReflect
, cUdpPacketReliable1, cUdpPacketReliable2, cUdpPacketReliable3, cUdpPacketReliable4
, cUdpPacketFragment1, cUdpPacketFragment2, cUdpPacketFragment3, cUdpPacketFragment4
, cUdpPacketAck1, cUdpPacketAck2, cUdpPacketAck3, cUdpPacketAck4
, cUdpPacketAckAll1, cUdpPacketAckAll2, cUdpPacketAckAll3, cUdpPacketAckAll4
, cUdpPacketGroup, cUdpPacketOrdered, cUdpPacketOrdered2, cUdpPacketPortAlive
, cUdpPacketUnreachableConnection, cUdpPacketRequestRemap };
};
/////////////////////////////////////////////////////////////////////////
// inline implementations
/////////////////////////////////////////////////////////////////////////
// UdpConnection
inline void UdpConnection::ScheduleTimeNow()
{
// if we are current in our GiveTime function getting time, then there is no need to reprioritize to 0 when we send a raw packet, since
// the last thing we do in out GiveTime is do a scheduling calculation based on the last time a packet was sent. This little check
// prevents us from reprioritizing to 0, only to shortly thereafter be reprioritized to where we actually belong.
if (!mGettingTime)
{
if (mUdpManager != NULL)
mUdpManager->SetPriority(this, 0);
}
}
inline void UdpConnection::SetHandler(UdpConnectionHandler *handler)
{
UdpGuard guard(&mHandlerGuard);
mHandler = handler;
}
inline UdpConnectionHandler *UdpConnection::GetHandler() const
{
UdpGuard guard(&mHandlerGuard);
return(mHandler);
}
inline bool UdpConnection::IsNonEncryptPacket(const udp_uchar *data) const
{
if (data[0] == 0)
{
if (data[1] == cUdpPacketConnect || data[1] == cUdpPacketConfirm || data[1] == cUdpPacketUnreachableConnection || data[1] == cUdpPacketRequestRemap)
return(true);
}
return(false);
}
inline int UdpConnection::GetEncryptCode() const
{
UdpGuard guard(&mGuard);
return(mConnectionConfig.encryptCode);
}
inline int UdpConnection::GetConnectCode() const
{
UdpGuard guard(&mGuard);
return(mConnectCode);
}
inline int UdpConnection::LastReceive(UdpClockStamp useStamp) const
{
UdpGuard guard(&mGuard);
return(UdpMisc::ClockDiff(mLastReceiveTime, useStamp));
}
inline int UdpConnection::LastReceive() const
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
return(mUdpManager->CachedClockElapsed(mLastReceiveTime));
}
inline int UdpConnection::ConnectionAge() const
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
return(mUdpManager->CachedClockElapsed(mConnectionCreateTime));
}
inline int UdpConnection::LastSend() const
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
return(mUdpManager->CachedClockElapsed(mLastSendTime));
}
inline udp_ushort UdpConnection::ServerSyncStampShort() const
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
return((udp_ushort)(mUdpManager->LocalSyncStampShort() + (mSyncTimeDelta & 0xffff)));
}
inline udp_uint UdpConnection::ServerSyncStampLong() const
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
return(mUdpManager->LocalSyncStampLong() + mSyncTimeDelta);
}
inline int UdpConnection::ServerSyncStampShortElapsed(udp_ushort syncStamp) const
{
return(UdpMisc::SyncStampShortDeltaTime(syncStamp, ServerSyncStampShort()));
}
inline int UdpConnection::ServerSyncStampLongElapsed(udp_uint syncStamp) const
{
return(UdpMisc::SyncStampLongDeltaTime(syncStamp, ServerSyncStampLong()));
}
inline UdpManager *UdpConnection::GetUdpManager() const
{
UdpGuard guard(&mGuard);
return(mUdpManager);
}
inline UdpConnection::Status UdpConnection::GetStatus() const
{
UdpGuard guard(&mGuard);
return(mStatus);
}
inline UdpConnection::DisconnectReason UdpConnection::GetDisconnectReason() const
{
UdpGuard guard(&mGuard);
return(mDisconnectReason);
}
inline UdpConnection::DisconnectReason UdpConnection::GetOtherSideDisconnectReason() const
{
UdpGuard guard(&mGuard);
return(mOtherSideDisconnectReason);
}
inline int UdpConnection::OutgoingBytesLastSecond()
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
ExpireSendBin();
return(mOutgoingBytesLastSecond);
}
inline int UdpConnection::IncomingBytesLastSecond()
{
UdpGuard guard(&mGuard);
if (mUdpManager == NULL)
return(0);
ExpireReceiveBin();
return(mIncomingBytesLastSecond);
}
inline void UdpConnection::SetPassThroughData(void *passThroughData)
{
UdpGuard guard(&mGuard);
mPassThroughData = passThroughData;
}
inline void *UdpConnection::GetPassThroughData() const
{
UdpGuard guard(&mGuard);
return(mPassThroughData);
}
inline UdpPlatformAddress UdpConnection::GetDestinationIp() const
{
UdpGuard guard(&mGuard);
return(mIp);
}
inline int UdpConnection::GetDestinationPort() const
{
UdpGuard guard(&mGuard);
return(mPort);
}
inline void UdpConnection::SetNoDataTimeout(int noDataTimeout)
{
UdpGuard guard(&mGuard);
mNoDataTimeout = noDataTimeout;
}
inline int UdpConnection::GetNoDataTimeout() const
{
UdpGuard guard(&mGuard);
return(mNoDataTimeout);
}
inline void UdpConnection::Disconnect(int flushTimeout)
{
UdpRef ref(this); // in case application releases us during the disconnect, we need to hold this reference so our guard object can be destroyed first
UdpGuard guard(&mGuard);
InternalDisconnect(flushTimeout, cDisconnectReasonApplication);
}
inline void UdpConnection::SetKeepAliveDelay(int keepAliveDelay)
{
UdpGuard guard(&mGuard);
mKeepAliveDelay = keepAliveDelay;
}
inline int UdpConnection::GetKeepAliveDelay() const
{
UdpGuard guard(&mGuard);
return(mKeepAliveDelay);
}
} // namespace
#endif
@@ -0,0 +1,124 @@
#ifndef UDPLIBRARY_UDPDRIVER_H
#define UDPLIBRARY_UDPDRIVER_H
#include "UdpTypes.h"
namespace UdpLibrary
{
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
class UdpDriver
{
public:
virtual ~UdpDriver() {}
// socket related functions
virtual bool SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress) = 0; // returns true if successful, false if it could not allocate/bind the socket to specified port
virtual void SocketClose() = 0;
virtual int SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port) = 0; // returns bytes read, -1 if no data, 0 for ICMP errors (from ip/port is filled in with who got the error)
virtual bool SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port) = 0;
virtual void SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port) = 0;
virtual bool SocketGetLocalIp(UdpPlatformAddress *ipAddress) = 0;
virtual int SocketGetLocalPort() = 0;
// non-socket related functions
virtual bool GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName) = 0;
virtual bool GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute) = 0;
virtual void Sleep(int milliseconds) = 0;
virtual UdpClockStamp Clock() = 0;
};
class UdpPlatformData;
class UdpPlatformDriver : public UdpDriver
{
public:
UdpPlatformDriver();
virtual ~UdpPlatformDriver();
// socket related functions
virtual bool SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress);
virtual void SocketClose();
virtual int SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port);
virtual bool SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port);
virtual void SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port);
virtual bool SocketGetLocalIp(UdpPlatformAddress *ipAddress);
virtual int SocketGetLocalPort();
// non-socket related functions that only the platform driver has
virtual bool GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName);
virtual bool GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute);
virtual void Sleep(int milliseconds);
virtual UdpClockStamp Clock();
private:
UdpPlatformData *mData;
};
class UdpPlatformGuardData;
class UdpPlatformGuardObject
{
public:
UdpPlatformGuardObject();
~UdpPlatformGuardObject();
void Enter();
void Leave();
private:
UdpPlatformGuardData *mData;
};
class UdpGuardedRefCount : public UdpRefCount
{
// portable: implemented in UdpLibrary.cpp
public:
virtual void AddRef() const;
virtual void Release() const;
protected:
mutable UdpPlatformGuardObject mGuard;
};
class UdpPlatformThreadData;
class UdpPlatformThreadObject : public UdpGuardedRefCount
{
public:
UdpPlatformThreadObject();
virtual void Start(); // starts thread running (can't do this in constructor because we need to give derived classes time to finish constructing)
virtual bool IsRunning();
virtual void Run() = 0;
protected:
virtual ~UdpPlatformThreadObject();
public:
UdpPlatformThreadData *mThreadData;
};
class UdpPlatformAddress
{
public:
UdpPlatformAddress();
UdpPlatformAddress(const UdpPlatformAddress &source);
bool operator==(const UdpPlatformAddress &e) const;
UdpPlatformAddress &operator=(const UdpPlatformAddress &e);
char *GetAddress(char *buffer, int bufferLen) const;
void SetAddress(const char *address);
int GetHash();
protected:
friend class UdpPlatformDriver;
// note: platforms are required to be able to store their representation of the address in these 4 bytes
// if we come across a platform that needs more space than this (or when we start doing IPv6), then we will
// increase this space a bit. This is sufficient for every platform so far and it avoids us having to
// do an allocation, particularly since these things are passed around by-value a lot.
unsigned char mData[4];
};
} // namespace
#endif
@@ -0,0 +1,467 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include "UdpDriver.h"
#include "UdpHelper.h"
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/ip_icmp.h> // needed by gcc 3.1 for linux
#include <pthread.h>
namespace UdpLibrary
{
typedef int SOCKET;
const int INVALID_SOCKET = 0xFFFFFFFF;
const int SOCKET_ERROR = 0xFFFFFFFF;
int IcmpReceive(SOCKET socket, unsigned *ipAddress, int *port);
////////////////////////////////////////////////////////////
// internal data definition
////////////////////////////////////////////////////////////
class UdpPlatformData
{
public:
SOCKET socket;
unsigned startTtl;
UdpPlatformGuardObject clockGuard;
UdpClockStamp lastStamp;
UdpClockStamp currentCorrection;
};
class UdpPlatformGuardData
{
public:
pthread_mutex_t mutex;
};
class UdpPlatformThreadData
{
public:
pthread_t handle;
bool running;
};
////////////////////////////////////////////////////////////
// platform specific implementation
////////////////////////////////////////////////////////////
UdpPlatformDriver::UdpPlatformDriver()
{
mData = new UdpPlatformData;
mData->socket = INVALID_SOCKET;
mData->startTtl = 32;
mData->currentCorrection = 0;
mData->lastStamp = 0;
}
UdpPlatformDriver::~UdpPlatformDriver()
{
delete mData;
}
bool UdpPlatformDriver::SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress)
{
mData->socket = socket(PF_INET, SOCK_DGRAM, 0);
if (mData->socket != INVALID_SOCKET)
{
// open socket stuff
unsigned long nb = 1;
int err = ioctl(mData->socket, FIONBIO, &nb);
assert(err != -1);
nb = outgoingBufferSize;
err = setsockopt(mData->socket, SOL_SOCKET, SO_SNDBUF, &nb, sizeof(nb));
assert(err == 0);
nb = incomingBufferSize;
err = setsockopt(mData->socket, SOL_SOCKET, SO_RCVBUF, &nb, sizeof(nb));
assert(err == 0);
nb = 0;
err = setsockopt(mData->socket, SOL_SOCKET, SO_BSDCOMPAT, &nb, sizeof(nb));
assert(err == 0);
nb = 1;
err = setsockopt(mData->socket, SOL_IP, IP_RECVERR, &nb, sizeof(nb));
assert(err == 0);
int optLen = sizeof(mData->startTtl);
getsockopt(mData->socket, IPPROTO_IP, IP_TTL, &mData->startTtl, (socklen_t *)&optLen);
// bind it to any address
struct sockaddr_in addr_loc;
addr_loc.sin_family = PF_INET;
addr_loc.sin_port = htons((unsigned short)port);
addr_loc.sin_addr.s_addr = htonl(INADDR_ANY);
if (bindIpAddress != NULL && bindIpAddress[0] != 0)
{
unsigned long address = inet_addr(bindIpAddress);
if (address != INADDR_NONE)
{
addr_loc.sin_addr.s_addr = address; // this is already in network order from the call above
}
}
if (bind(mData->socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0)
{
SocketClose();
return(false);
}
}
else
{
return(false);
}
return(true);
}
void UdpPlatformDriver::SocketClose()
{
if (mData->socket != INVALID_SOCKET)
{
close(mData->socket);
mData->socket = INVALID_SOCKET;
}
}
int UdpPlatformDriver::SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port)
{
// check for standard packets
struct sockaddr_in addr_from;
socklen_t sf = sizeof(addr_from);
int res = recvfrom(mData->socket, buffer, bufferSize, 0, (struct sockaddr *)&addr_from, &sf);
if (res != SOCKET_ERROR)
{
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
*port = (int)ntohs(addr_from.sin_port);
return(res);
}
// out of standard packets, check for ICMP error packets
unsigned address;
int ret = IcmpReceive(mData->socket, &address, port);
memcpy(ipAddress->mData, &address, 4);
return(ret);
}
bool UdpPlatformDriver::SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
{
struct sockaddr_in addr_dest;
addr_dest.sin_family = PF_INET;
memcpy(&addr_dest.sin_addr.s_addr, ipAddress->mData, 4);
addr_dest.sin_port = htons((unsigned short)port);
if (SOCKET_ERROR == sendto(mData->socket, data, dataLen, 0, (struct sockaddr *)&addr_dest, sizeof(addr_dest)))
{
return(false);
}
return(true);
}
void UdpPlatformDriver::SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
{
// port alive packet send
unsigned long val = 5;
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val));
SocketSend(data, dataLen, ipAddress, port);
val = mData->startTtl;
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val));
}
bool UdpPlatformDriver::SocketGetLocalIp(UdpPlatformAddress *ipAddress)
{
struct sockaddr_in addr_self;
memset(&addr_self, 0, sizeof(addr_self));
socklen_t len = sizeof(addr_self);
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
memcpy(ipAddress->mData, &addr_self.sin_addr.s_addr, 4);
return(true);
}
int UdpPlatformDriver::SocketGetLocalPort()
{
struct sockaddr_in addr_self;
memset(&addr_self, 0, sizeof(addr_self));
socklen_t len = sizeof(addr_self);
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
return(ntohs(addr_self.sin_port));
}
bool UdpPlatformDriver::GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName)
{
unsigned long address = inet_addr(hostName);
if (address == INADDR_NONE)
{
struct hostent *lphp;
lphp = gethostbyname(hostName);
if (lphp == NULL)
{
address = 0;
}
else
{
address = ((struct in_addr *)(lphp->h_addr))->s_addr;
}
}
memcpy(ipAddress->mData, &address, 4);
return(address != 0);
}
bool UdpPlatformDriver::GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute)
{
unsigned long address = 0;
char hostname[1024];
if (gethostname(hostname, sizeof(hostname)) == 0)
{
struct hostent *entry = gethostbyname(hostname);
if (entry != NULL)
{
for (int i = 0; entry->h_addr_list[i] != 0; i++)
{
in_addr *sin = (in_addr *)entry->h_addr_list[i];
int s_net = (sin->s_addr >> 24);
int s_host = ((sin->s_addr >> 16) & 0xff);
if (address == 0)
{
address = sin->s_addr;
}
else if (s_net != 127) // never return 127.0.0.1 as self address if there is another option
{
bool routeable = true;
if (s_net == 10 || (s_net == 172 && s_host == 16) || (s_net == 192 && s_host == 168))
{
routeable = false;
}
if (preferNoRoute && !routeable)
{
address = sin->s_addr;
}
else if (!preferNoRoute && routeable)
{
address = sin->s_addr;
}
}
}
}
}
memcpy(ipAddress->mData, &address, 4);
return(address != 0);
}
void UdpPlatformDriver::Sleep(int milliseconds)
{
struct timeval tv;
tv.tv_sec = milliseconds / 1000;
tv.tv_usec = (milliseconds % 1000) * 1000;
select(0, 0, 0, 0, &tv);
}
UdpClockStamp UdpPlatformDriver::Clock()
{
UdpGuard guard(&mData->clockGuard);
struct timeval tv;
gettimeofday(&tv, NULL);
UdpClockStamp cs = static_cast<UdpClockStamp>(tv.tv_sec) * 1000 + static_cast<UdpClockStamp>(tv.tv_usec / 1000);
cs += mData->currentCorrection;
if (cs < mData->lastStamp)
{
// clock moved backwards (somebody changed it), don't ever let this happen
// if clock moves forward, there is no way we can recognize it, code will just
// have to deal with it. In the case of the UdpLibrary, it will likely result
// in a ton of pending packets thinking they have gotten lost and being sent, fairly harmless.
mData->currentCorrection += (mData->lastStamp - cs);
cs = mData->lastStamp;
}
mData->lastStamp = cs;
return(cs);
}
int IcmpReceive(SOCKET socket, unsigned *address, int *port)
{
// we can use some ICMP errors to our advantage to more quickly realize that the connection on the other end has disappeared
struct sock_extended_err
{
unsigned ee_errno;
unsigned char ee_origin;
unsigned char ee_type;
unsigned char ee_code;
unsigned char ee_pad;
unsigned ee_info;
unsigned ee_data;
};
unsigned char msg_control[1024];
struct msghdr msgh = {0};
struct sockaddr_in msg_name;
socklen_t sf = sizeof(msg_name);
msgh.msg_name = &msg_name;
msgh.msg_namelen = sf;
msgh.msg_iov = 0;
msgh.msg_iovlen = 0;
msgh.msg_control = msg_control;
msgh.msg_controllen = sizeof(msg_control);
int err = recvmsg(socket, &msgh, MSG_ERRQUEUE);
if (err == -1)
return(-1);
struct cmsghdr *cmsg;
for(cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg))
{
if(cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
{
sock_extended_err *ee = (sock_extended_err *)CMSG_DATA(cmsg);
if (ee->ee_origin == 2 && ee->ee_type == ICMP_DEST_UNREACH) // ICMP origin, destination-unreachable error // note: process all code types: && ee->ee_code == ICMP_PORT_UNREACH
{
memcpy(address, &msg_name.sin_addr.s_addr, 4);
*port = (int)ntohs(msg_name.sin_port);
return(0); // ICMP error return
}
}
}
return(-1);
}
////////////////////////////////////////////////////////////
// UdpPlatformGuardObject object implementation
////////////////////////////////////////////////////////////
UdpPlatformGuardObject::UdpPlatformGuardObject()
{
mData = new UdpPlatformGuardData;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mData->mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
UdpPlatformGuardObject::~UdpPlatformGuardObject()
{
pthread_mutex_destroy(&mData->mutex);
delete mData;
}
void UdpPlatformGuardObject::Enter()
{
pthread_mutex_lock(&mData->mutex);
}
void UdpPlatformGuardObject::Leave()
{
pthread_mutex_unlock(&mData->mutex);
}
///////////////////////////////////////////////////////////////
// UdpPlatformThreadObject implementation
///////////////////////////////////////////////////////////////
void *GoThread(void *param)
{
UdpPlatformThreadObject *thread = (UdpPlatformThreadObject *)param;
thread->mThreadData->running = true;
thread->Run();
thread->mThreadData->running = false;
thread->mThreadData->handle = 0;
thread->Release();
return(NULL);
}
UdpPlatformThreadObject::~UdpPlatformThreadObject()
{
delete mThreadData;
}
void UdpPlatformThreadObject::Start()
{
AddRef();
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&mThreadData->handle, &attr, &GoThread, (void *)this);
pthread_attr_destroy(&attr);
}
UdpPlatformThreadObject::UdpPlatformThreadObject()
{
mThreadData = new UdpPlatformThreadData;
mThreadData->handle = 0;
mThreadData->running = false;
}
bool UdpPlatformThreadObject::IsRunning()
{
return(mThreadData->running);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// UdpPlatformAddress implementation
/////////////////////////////////////////////////////////////////////////////////////////////////////
UdpPlatformAddress::UdpPlatformAddress()
{
memset(mData, 0, sizeof(mData));
}
UdpPlatformAddress::UdpPlatformAddress(const UdpPlatformAddress &source)
{
memcpy(mData, source.mData, sizeof(mData));
}
UdpPlatformAddress &UdpPlatformAddress::operator=(const UdpPlatformAddress &e)
{
memcpy(mData, e.mData, sizeof(mData));
return(*this);
}
char *UdpPlatformAddress::GetAddress(char *buffer, int bufferLen) const
{
if (bufferLen < 16)
{
*buffer = 0;
return(buffer);
}
assert(buffer != NULL);
sprintf(buffer, "%d.%d.%d.%d", mData[0], mData[1], mData[2], mData[3]);
return(buffer);
}
void UdpPlatformAddress::SetAddress(const char *address)
{
for (int i = 0; i < 4; i++)
{
mData[i] = (unsigned char)strtol(address, NULL, 10);
while (*address >= '0' && *address <= '9')
address++;
if (*address != 0)
address++;
}
}
bool UdpPlatformAddress::operator==(const UdpPlatformAddress &e) const
{
return(memcmp(mData, e.mData, sizeof(mData)) == 0);
}
int UdpPlatformAddress::GetHash()
{
return(*(int *)mData);
}
} // namespace
@@ -0,0 +1,421 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#if defined(WIN32)
#define _CRT_SECURE_NO_DEPRECATE // gets rid of deprecation warnings in VS 2005 (don't want to change to secure-versions as it hampers portability)
#endif
#include "UdpDriver.h"
#include "UdpHelper.h"
#ifdef WIN32
#pragma warning(push, 3)
#pragma warning(disable:4706)
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#ifndef UDPLIBRARY_SINGLE_THREAD
#include <process.h>
#endif
#pragma warning(pop)
namespace UdpLibrary
{
typedef int socklen_t;
////////////////////////////////////////////////////////////
// internal data definition
////////////////////////////////////////////////////////////
class UdpPlatformData
{
public:
SOCKET socket;
unsigned startTtl;
UdpPlatformGuardObject clockGuard;
unsigned globalLow;
int globalHigh;
};
class UdpPlatformGuardData
{
public:
CRITICAL_SECTION mCriticalSection;
};
class UdpPlatformThreadData
{
public:
HANDLE handle;
bool running;
};
////////////////////////////////////////////////////////////
// platform specific implementation
////////////////////////////////////////////////////////////
UdpPlatformDriver::UdpPlatformDriver()
{
mData = new UdpPlatformData;
mData->socket = INVALID_SOCKET;
mData->startTtl = 32;
mData->globalLow = 0;
mData->globalHigh = 0;
WSADATA wsaData;
#if defined(UDPLIBRARY_WINSOCK2)
WSAStartup(MAKEWORD(2,0), &wsaData);
#else
WSAStartup(MAKEWORD(1,1), &wsaData);
#endif
}
UdpPlatformDriver::~UdpPlatformDriver()
{
WSACleanup();
delete mData;
}
bool UdpPlatformDriver::SocketOpen(int port, int incomingBufferSize, int outgoingBufferSize, const char *bindIpAddress)
{
mData->socket = socket(PF_INET, SOCK_DGRAM, 0);
if (mData->socket != INVALID_SOCKET)
{
unsigned long lb = 1;
int err = ioctlsocket(mData->socket, FIONBIO, &lb);
int nb = outgoingBufferSize;
err = setsockopt(mData->socket, SOL_SOCKET, SO_SNDBUF, (char *)&nb, sizeof(nb));
nb = incomingBufferSize;
err = setsockopt(mData->socket, SOL_SOCKET, SO_RCVBUF, (char *)&nb, sizeof(nb));
int optLen = sizeof(mData->startTtl);
getsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&mData->startTtl, &optLen);
// bind it to any address
struct sockaddr_in addr_loc;
addr_loc.sin_family = PF_INET;
addr_loc.sin_port = htons((unsigned short)port);
addr_loc.sin_addr.s_addr = htonl(INADDR_ANY);
if (bindIpAddress != NULL && bindIpAddress[0] != 0)
{
unsigned long address = inet_addr(bindIpAddress);
if (address != INADDR_NONE)
{
addr_loc.sin_addr.s_addr = address; // this is already in network order from the call above
}
}
if (bind(mData->socket, (struct sockaddr *)&addr_loc, sizeof(addr_loc)) != 0)
{
SocketClose();
return(false);
}
}
else
{
return(false);
}
return(true);
}
void UdpPlatformDriver::SocketClose()
{
if (mData->socket != INVALID_SOCKET)
{
closesocket(mData->socket);
mData->socket = INVALID_SOCKET;
}
}
int UdpPlatformDriver::SocketReceive(char *buffer, int bufferSize, UdpPlatformAddress *ipAddress, int *port)
{
struct sockaddr_in addr_from;
socklen_t sf = sizeof(addr_from);
int res = recvfrom(mData->socket, buffer, bufferSize, 0, (struct sockaddr *)&addr_from, &sf);
if (res != SOCKET_ERROR)
{
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
*port = (int)ntohs(addr_from.sin_port);
return(res);
}
// windows is kind enough to put ICMP error packets inline within the stream as errors, so we
// can easily see the errors indicating that the destination address is unreachable for some reason
if (WSAGetLastError() == WSAECONNRESET)
{
memcpy(ipAddress->mData, &addr_from.sin_addr.s_addr, 4);
*port = (int)ntohs(addr_from.sin_port);
return(0);
}
return(-1); // no more packets found
}
bool UdpPlatformDriver::SocketSend(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
{
struct sockaddr_in addr_dest;
addr_dest.sin_family = PF_INET;
memcpy(&addr_dest.sin_addr.s_addr, ipAddress->mData, 4);
addr_dest.sin_port = htons((unsigned short)port);
if (SOCKET_ERROR == sendto(mData->socket, data, dataLen, 0, (struct sockaddr *)&addr_dest, sizeof(addr_dest)))
{
return(false);
}
return(true);
}
void UdpPlatformDriver::SocketSendPortAlive(const char *data, int dataLen, const UdpPlatformAddress *ipAddress, int port)
{
int val = 5;
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&val, sizeof(val));
SocketSend(data, dataLen, ipAddress, port);
setsockopt(mData->socket, IPPROTO_IP, IP_TTL, (char *)&mData->startTtl, sizeof(mData->startTtl));
}
bool UdpPlatformDriver::SocketGetLocalIp(UdpPlatformAddress *ipAddress)
{
struct sockaddr_in addr_self;
memset(&addr_self, 0, sizeof(addr_self));
socklen_t len = sizeof(addr_self);
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
memcpy(ipAddress->mData, &addr_self.sin_addr.s_addr, 4);
return(true);
}
int UdpPlatformDriver::SocketGetLocalPort()
{
struct sockaddr_in addr_self;
memset(&addr_self, 0, sizeof(addr_self));
socklen_t len = sizeof(addr_self);
getsockname(mData->socket, (struct sockaddr *)&addr_self, &len);
return(ntohs(addr_self.sin_port));
}
bool UdpPlatformDriver::GetHostByName(UdpPlatformAddress *ipAddress, const char *hostName)
{
unsigned long address = inet_addr(hostName);
if (address == INADDR_NONE)
{
struct hostent *lphp;
lphp = gethostbyname(hostName);
if (lphp == NULL)
{
address = 0;
}
else
{
address = ((struct in_addr *)(lphp->h_addr))->s_addr;
}
}
memcpy(ipAddress->mData, &address, 4);
return(address != 0);
}
bool UdpPlatformDriver::GetSelfAddress(UdpPlatformAddress *ipAddress, bool preferNoRoute)
{
unsigned long address = 0;
char hostname[1024];
if (gethostname(hostname, sizeof(hostname)) == 0)
{
struct hostent *entry = gethostbyname(hostname);
if (entry != NULL)
{
for (int i = 0; entry->h_addr_list[i] != 0; i++)
{
in_addr *sin = (in_addr *)entry->h_addr_list[i];
if (address == 0)
{
address = sin->s_addr;
}
else if (sin->s_net != 127) // never return 127.0.0.1 as self address if there is another option
{
bool routeable = true;
if (sin->s_net == 10 || (sin->s_net == 172 && sin->s_host == 16) || (sin->s_net == 192 && sin->s_host == 168))
{
routeable = false;
}
if (preferNoRoute && !routeable)
{
address = sin->s_addr;
}
else if (!preferNoRoute && routeable)
{
address = sin->s_addr;
}
}
}
}
}
memcpy(ipAddress->mData, &address, 4);
return(address != 0);
}
void UdpPlatformDriver::Sleep(int milliseconds)
{
::Sleep((DWORD)milliseconds);
}
UdpClockStamp UdpPlatformDriver::Clock()
{
UdpGuard guard(&mData->clockGuard);
unsigned low = GetTickCount();
if (low < mData->globalLow)
{
mData->globalHigh++;
}
mData->globalLow = low;
return(((UdpClockStamp)mData->globalHigh << 32) | low);
}
////////////////////////////////////////////////////////////
// UdpPlatformGuardObject object implementation
////////////////////////////////////////////////////////////
UdpPlatformGuardObject::UdpPlatformGuardObject()
{
#ifndef UDPLIBRARY_SINGLE_THREAD
mData = new UdpPlatformGuardData;
InitializeCriticalSection(&mData->mCriticalSection);
#endif
}
UdpPlatformGuardObject::~UdpPlatformGuardObject()
{
#ifndef UDPLIBRARY_SINGLE_THREAD
DeleteCriticalSection(&mData->mCriticalSection);
delete mData;
#endif
}
void UdpPlatformGuardObject::Enter()
{
#ifndef UDPLIBRARY_SINGLE_THREAD
EnterCriticalSection(&mData->mCriticalSection);
#endif
}
void UdpPlatformGuardObject::Leave()
{
#ifndef UDPLIBRARY_SINGLE_THREAD
LeaveCriticalSection(&mData->mCriticalSection);
#endif
}
///////////////////////////////////////////////////////////////
// UdpPlatformThreadObject implementation
///////////////////////////////////////////////////////////////
unsigned __stdcall GoThread(void *param)
{
UdpPlatformThreadObject *thread = (UdpPlatformThreadObject *)param;
thread->mThreadData->running = true;
thread->Run();
thread->mThreadData->running = false;
thread->Release();
return(0);
}
UdpPlatformThreadObject::~UdpPlatformThreadObject()
{
#ifndef UDPLIBRARY_SINGLE_THREAD
if (mThreadData->handle != NULL)
{
CloseHandle(mThreadData->handle);
mThreadData->handle = NULL;
}
#endif
delete mThreadData;
}
void UdpPlatformThreadObject::Start()
{
AddRef();
#ifndef UDPLIBRARY_SINGLE_THREAD
unsigned threadId;
mThreadData->handle = (HANDLE)_beginthreadex(NULL, 0, &GoThread, this, 0, &threadId);
#else
GoThread(this); // run it inline in main thread (blocks til it's finished, so odds are it won't work, but they shouldn't be using it anyhow in this mode)
#endif
}
UdpPlatformThreadObject::UdpPlatformThreadObject()
{
mThreadData = new UdpPlatformThreadData;
mThreadData->handle = NULL;
mThreadData->running = false;
}
bool UdpPlatformThreadObject::IsRunning()
{
return(mThreadData->running);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// UdpPlatformAddress implementation
/////////////////////////////////////////////////////////////////////////////////////////////////////
UdpPlatformAddress::UdpPlatformAddress()
{
memset(mData, 0, sizeof(mData));
}
UdpPlatformAddress::UdpPlatformAddress(const UdpPlatformAddress &source)
{
memcpy(mData, source.mData, sizeof(mData));
}
UdpPlatformAddress &UdpPlatformAddress::operator=(const UdpPlatformAddress &e)
{
memcpy(mData, e.mData, sizeof(mData));
return(*this);
}
char *UdpPlatformAddress::GetAddress(char *buffer, int bufferLen) const
{
if (bufferLen < 16)
{
*buffer = 0;
return(buffer);
}
assert(buffer != NULL);
sprintf(buffer, "%d.%d.%d.%d", mData[0], mData[1], mData[2], mData[3]);
return(buffer);
}
void UdpPlatformAddress::SetAddress(const char *address)
{
for (int i = 0; i < 4; i++)
{
mData[i] = (unsigned char)atoi(address);
while (*address >= '0' && *address <= '9')
address++;
if (*address != 0)
address++;
}
}
bool UdpPlatformAddress::operator==(const UdpPlatformAddress &e) const
{
return(memcmp(mData, e.mData, sizeof(mData)) == 0);
}
int UdpPlatformAddress::GetHash()
{
return(*(int *)mData);
}
} // namespace
#endif // WIN32
@@ -0,0 +1,70 @@
#ifndef UDPLIBRARY_UDPHANDLER_H
#define UDPLIBRARY_UDPHANDLER_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include "UdpTypes.h"
namespace UdpLibrary
{
class UdpManagerHandler
{
public:
// This function is called when a listening UdpManager receives a new connection request. If the application wishes to
// accept this connection, it is the responsibility of the application to AddRef the UdpConnection object such that it
// doesn't get destroyed upon returning from the callback. Additionally, the application should set the handler object
// for the connection. See sample code in the UdpLibrary.doc file for an example.
virtual void OnConnectRequest(UdpConnection *con);
// The following functions are called when user-supplied encryption is specified. In particular, the functions may wish
// to query the connection object for the encryption-code it is using. The encryption code is simply a randomly generated
// 32-bit value that the client and server negotiated during the connection-establishment. It can be used as a key
// of sorts for encrypting data uniquely on a per-connection basis. (see UdpConnection::GetEncryptCode())
// For the encrypt-functions, the destination buffer will be (sourceLen + userSuppliedEncryptExpansionBytes1) long,
// do not overflow it, the debug version has an assert check that ensures that you do not.
// For the decrypt-functions, the destination buffer is guaranteed to be large enough to hold what the original
// data used to be before it was encrypted.
virtual int OnUserSuppliedEncrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
virtual int OnUserSuppliedDecrypt(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
virtual int OnUserSuppliedEncrypt2(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
virtual int OnUserSuppliedDecrypt2(UdpConnection *con, udp_uchar *destData, const udp_uchar *sourceData, int sourceLen);
};
class UdpConnectionHandler
{
public:
// This function is called whenever an application-packet is ready to be delivered to the application.
// Packets will only ever be delivered to the application during UdpManager::GiveTime calls, so the application
// has complete control of when it willing/able to receive packets.
virtual void OnRoutePacket(UdpConnection *con, const udp_uchar *data, int dataLen) = 0;
// This function is called whenever a UdpManager::EstablishConnection call succeeds in establishing the connection.
// Generally on client-side establishment of a connection, I prefer to sit in a tight loop polling the connection
// to see if it has connected yet or not, rather than using this callback (see sample code)
// note: If it is unable to establish the connection, the OnTerminated callback will be called.
// note: this is a change from previous behavior, see release notes for details.
virtual void OnConnectComplete(UdpConnection *con);
// This function is called anytime the UdpConnection object changes to a cStatusDisconnected state. Every UdpConnection
// object is guaranteed to eventually call this once and only once, even connections that never successfully established.
// note: this is a change from previous behavior, see release notes for details
virtual void OnTerminated(UdpConnection *con);
// This function is called anytime an incoming packet fails the CRC check. Normally these packets are just ignored, as
// corruption does occur from time to time; however, some applications may wish to log these events as often times, they
// are an indication of somebody attempting to hack the data stream.
virtual void OnCrcReject(UdpConnection *con, const udp_uchar *data, int dataLen);
// This function is called anytime a corrupt packet is sensed for some reason.
// The reason we give a callback for it is because you may wish to log it as it could be an early indicator of
// somebody trying to hack the protocol.
virtual void OnPacketCorrupt(UdpConnection *con, const udp_uchar *data, int dataLen, UdpCorruptionReason reason);
};
} // namespace
#endif
@@ -0,0 +1,625 @@
#ifndef UDPLIBRARY_UDPHASHTABLE_H
#define UDPLIBRARY_UDPHASHTABLE_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <stddef.h>
#include <string.h>
namespace UdpLibrary
{
int NextPrime(int value);
// This is a simple hash table template. Any type of object can be stored in this hash table
// provided it can be copied. Resizing the table while in use is generally not recommended.
// putting the same object in the table twice is generally not recommended as it makes the find ambiguous
struct HashTableStatistics
{
int tableSize;
int usedSlots;
int totalEntries;
};
//////////////////////////////////////////////////////////////////////////
// HashTable
//////////////////////////////////////////////////////////////////////////
template<typename T> class HashTable
{
public:
HashTable(int hashSize);
~HashTable();
void Insert(T& obj, int hashValue);
bool Remove(T& obj, int hashValue);
void Reset(); // removes all entries from the table
T *FindFirst(int hashValue) const; // returns NULL if not found
T *FindNext(T *prevResult) const; // returns NULL if not found
T *WalkFirst() const; // returns NULL if not found
T *WalkNext(T *prevResult) const; // returns NULL if not found
void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended)
void GetStatistics(HashTableStatistics *stats) const;
protected:
struct HashEntry
{
T obj;
int hashValue;
HashEntry *nextEntry;
};
HashEntry **mTable;
int mTableSize;
int mEntryCount;
int mStatUsedSlots;
};
template<typename T> HashTable<T>::HashTable(int hashSize)
{
mTable = NULL;
mTableSize = 0;
mEntryCount = 0;
mStatUsedSlots = 0;
Resize(hashSize);
}
template<typename T> HashTable<T>::~HashTable()
{
Reset(); // deletes everything
delete[] mTable;
}
template<typename T> void HashTable<T>::Insert(T& obj, int hashValue)
{
HashEntry *entry = new HashEntry;
entry->obj = obj;
entry->hashValue = hashValue;
int spot = ((unsigned)hashValue) % mTableSize;
if (mTable[spot] == NULL)
{
entry->nextEntry = NULL;
mTable[spot] = entry;
mStatUsedSlots++;
}
else
{
entry->nextEntry = mTable[spot];
mTable[spot] = entry;
}
mEntryCount++;
}
template<typename T> bool HashTable<T>::Remove(T& obj, int hashValue)
{
int spot = ((unsigned)hashValue) % mTableSize;
HashEntry* next = mTable[spot];
HashEntry** prev = &mTable[spot];
while (next != NULL)
{
if (next->obj == obj && next->hashValue == hashValue)
{
*prev = next->nextEntry;
delete next;
mEntryCount--;
if (mTable[spot] == NULL)
mStatUsedSlots--;
return(true);
break;
}
prev = &next->nextEntry;
next = next->nextEntry;
}
return(false);
}
template<typename T> void HashTable<T>::Reset()
{
for (int spot = 0; spot < mTableSize; spot++)
{
HashEntry *curr = mTable[spot];
if (curr != NULL)
{
mStatUsedSlots--;
while (curr != NULL)
{
HashEntry *next = curr->nextEntry;
delete curr;
mEntryCount--;
curr = next;
}
mTable[spot] = NULL;
}
}
}
template<typename T> T *HashTable<T>::FindFirst(int hashValue) const
{
HashEntry *entry = mTable[((unsigned)hashValue) % mTableSize];
while (entry != NULL)
{
if (entry->hashValue == hashValue)
return(&entry->obj);
entry = entry->nextEntry;
}
return(NULL);
}
template<typename T> T *HashTable<T>::FindNext(T *prevResult) const
{
HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj));
int hashValue = entry->hashValue;
entry = entry->nextEntry;
while (entry != NULL)
{
if (entry->hashValue == hashValue)
return(&entry->obj);
entry = entry->nextEntry;
}
return(NULL);
}
template<typename T> T *HashTable<T>::WalkFirst() const
{
for (int bucket = 0; bucket < mTableSize; bucket++)
{
HashEntry *entry = mTable[bucket];
if (entry != NULL)
return(&entry->obj);
}
return(NULL);
}
template<typename T> T *HashTable<T>::WalkNext(T *prevResult) const
{
HashEntry *entry = (HashEntry *)(((char *)prevResult) - offsetof(HashEntry, obj));
int bucket = ((unsigned)entry->hashValue) % mTableSize;
entry = entry->nextEntry;
if (entry != NULL)
return(&entry->obj);
bucket++; // go onto next bucket
for (; bucket < mTableSize; bucket++)
{
HashEntry *entry = mTable[bucket];
if (entry != NULL)
return(&entry->obj);
}
return(NULL);
}
template<typename T> void HashTable<T>::Resize(int hashSize)
{
HashEntry **oldTable = mTable;
int oldSize = mTableSize;
// calculate next prime number greater than hashSize.
mTableSize = NextPrime(hashSize);
mTable = new HashEntry*[mTableSize];
memset(mTable, 0, sizeof(HashEntry *) * mTableSize);
mStatUsedSlots = 0;
if (mEntryCount > 0)
{
for (int i = 0; i < oldSize; i++)
{
HashEntry* next = oldTable[i];
while (next != NULL)
{
HashEntry* hold = next;
next = next->nextEntry;
// insert hold into new table
int spot = ((unsigned)hold->hashValue) % mTableSize;
if (mTable[spot] == NULL)
{
hold->nextEntry = NULL;
mTable[spot] = hold;
mStatUsedSlots++;
}
else
{
hold->nextEntry = mTable[spot];
mTable[spot] = hold;
}
}
}
}
delete[] oldTable;
}
template<typename T> void HashTable<T>::GetStatistics(HashTableStatistics *stats) const
{
stats->totalEntries = mEntryCount;
stats->usedSlots = mStatUsedSlots;
stats->tableSize = mTableSize;
}
//////////////////////////////////////////////////////////////////////////////
// ObjectHashTable
//
// similar to basic HashTable object, only this version requires the contents
// be pointers to objects derived from class HashTableMember. If you can use
// this version, it avoids allocations of HashEntry objects.
// making the cost of object inserts/deletes dirt-cheap
//////////////////////////////////////////////////////////////////////////////
template<typename M> class HashTableMember
{
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
public:
#else
protected:
template<typename T, typename P> friend class ObjectHashTable;
#endif
int mHashValue;
M *mHashNextEntry;
};
template<typename M> class HashTableMember1 : public HashTableMember<M> {};
template<typename M> class HashTableMember2 : public HashTableMember<M> {};
template<typename M> class HashTableMember3 : public HashTableMember<M> {};
template<typename M> class HashTableMember4 : public HashTableMember<M> {};
template<typename T, typename C = T> class ObjectHashTable
{
public:
ObjectHashTable(int hashSize);
~ObjectHashTable();
void Insert(T *obj, int hashValue);
bool Remove(T *obj);
void Reset(); // removes all entries from the table
T *FindFirst(int hashValue) const; // returns NULL if not found
T *FindNext(T *prevResult) const; // returns NULL if not found
T *WalkFirst() const; // returns NULL if not found
T *WalkNext(T *prevResult) const; // returns NULL if not found
void Resize(int hashSize); // the actual hash size will be rounded up to the next larger prime number (very slow, not recommended)
void GetStatistics(HashTableStatistics *stats) const;
protected:
T **mTable;
int mTableSize;
int mEntryCount;
int mStatUsedSlots;
};
template<typename T, typename C> ObjectHashTable<T, C>::ObjectHashTable(int hashSize)
{
mTable = NULL;
mTableSize = 0;
mEntryCount = 0;
mStatUsedSlots = 0;
Resize(hashSize);
}
template<typename T, typename C> ObjectHashTable<T, C>::~ObjectHashTable()
{
delete[] mTable;
}
template<typename T, typename C> void ObjectHashTable<T, C>::Insert(T *obj, int hashValue)
{
static_cast<C *>(obj)->mHashValue = hashValue;
int spot = ((unsigned)hashValue) % mTableSize;
if (mTable[spot] == NULL)
{
static_cast<C *>(obj)->mHashNextEntry = NULL;
mTable[spot] = obj;
mStatUsedSlots++;
}
else
{
static_cast<C *>(obj)->mHashNextEntry = mTable[spot];
mTable[spot] = obj;
}
mEntryCount++;
}
template<typename T, typename C> bool ObjectHashTable<T, C>::Remove(T *obj)
{
int spot = ((unsigned)static_cast<C *>(obj)->mHashValue) % mTableSize;
T *cur = mTable[spot];
T **prev = &mTable[spot];
while (cur != NULL)
{
if (cur == obj)
{
*prev = static_cast<C *>(cur)->mHashNextEntry;
static_cast<C *>(cur)->mHashNextEntry = NULL;
mEntryCount--;
if (mTable[spot] == NULL)
mStatUsedSlots--;
return(true);
break;
}
prev = &static_cast<C *>(cur)->mHashNextEntry;
cur = static_cast<C *>(cur)->mHashNextEntry;
}
return(false);
}
template<typename T, typename C> void ObjectHashTable<T, C>::Reset()
{
memset(mTable, 0, sizeof(T *) * mTableSize);
mStatUsedSlots = 0;
mEntryCount = 0;
}
template<typename T, typename C> T *ObjectHashTable<T, C>::FindFirst(int hashValue) const
{
T *entry = mTable[((unsigned)hashValue) % mTableSize];
while (entry != NULL)
{
if (static_cast<C *>(entry)->mHashValue == hashValue)
return(entry);
entry = static_cast<C *>(entry)->mHashNextEntry;
}
return(NULL);
}
template<typename T, typename C> T *ObjectHashTable<T, C>::FindNext(T *prevResult) const
{
T *entry = prevResult;
int hashValue = static_cast<C *>(entry)->mHashValue;
entry = static_cast<C *>(entry)->mHashNextEntry;
while (entry != NULL)
{
if (static_cast<C *>(entry)->mHashValue == hashValue)
return(entry);
entry = static_cast<C *>(entry)->mHashNextEntry;
}
return(NULL);
}
template<typename T, typename C> T *ObjectHashTable<T, C>::WalkFirst() const
{
for (int bucket = 0; bucket < mTableSize; bucket++)
{
T *entry = mTable[bucket];
if (entry != NULL)
return(entry);
}
return(NULL);
}
template<typename T, typename C> T *ObjectHashTable<T, C>::WalkNext(T *prevResult) const
{
T *entry = prevResult;
int bucket = ((unsigned)static_cast<C *>(entry)->mHashValue) % mTableSize;
entry = static_cast<C *>(entry)->mHashNextEntry;
if (entry != NULL)
return(entry);
bucket++; // go onto next bucket
for (; bucket < mTableSize; bucket++)
{
entry = mTable[bucket];
if (entry != NULL)
return(entry);
}
return(NULL);
}
template<typename T, typename C> void ObjectHashTable<T, C>::Resize(int hashSize)
{
T **oldTable = mTable;
int oldSize = mTableSize;
// calculate next prime number greater than hashSize.
mTableSize = NextPrime(hashSize);
mTable = new T*[mTableSize];
memset(mTable, 0, sizeof(T *) * mTableSize);
mStatUsedSlots = 0;
if (mEntryCount > 0)
{
for (int i = 0; i < oldSize; i++)
{
T *cur = oldTable[i];
while (cur != NULL)
{
T *hold = cur;
cur = static_cast<C *>(cur)->mHashNextEntry;
// insert hold into new table
int spot = ((unsigned)static_cast<C *>(hold)->mHashValue) % mTableSize;
if (mTable[spot] == NULL)
{
static_cast<C *>(hold)->mHashNextEntry = NULL;
mTable[spot] = hold;
mStatUsedSlots++;
}
else
{
static_cast<C *>(hold)->mHashNextEntry = mTable[spot];
mTable[spot] = hold;
}
}
}
}
delete[] oldTable;
}
template<typename T, typename C> void ObjectHashTable<T, C>::GetStatistics(HashTableStatistics *stats) const
{
stats->totalEntries = mEntryCount;
stats->usedSlots = mStatUsedSlots;
stats->tableSize = mTableSize;
}
////////////////////////////////////////////////////////
// helper functions for the above hash table classes
////////////////////////////////////////////////////////
inline bool IsPrime(int value) // works on positive numbers up 62,710,561
{
static int sPrimeTab[] =
{ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129,
2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213,
2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287,
2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423,
2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617,
2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741,
2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819,
2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903,
2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079,
3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181,
3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257,
3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511,
3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571,
3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727,
3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821,
3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907,
3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057,
4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139,
4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231,
4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409,
4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493,
4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583,
4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751,
4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831,
4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937,
4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003,
5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087,
5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179,
5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387,
5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443,
5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521,
5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639,
5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693,
5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791,
5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857,
5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939,
5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053,
6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133,
6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301,
6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367,
6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473,
6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571,
6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673,
6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761,
6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833,
6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917,
6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997,
7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103,
7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297,
7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411,
7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499,
7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561,
7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643,
7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723,
7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829,
7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919
};
for (unsigned int j = 0; j < sizeof(sPrimeTab) / sizeof(int) && sPrimeTab[j] * sPrimeTab[j] < value; j++)
{
if (value % sPrimeTab[j] == 0)
return(false);
}
return(true);
}
inline int NextPrime(int value)
{
if (value % 2 == 0)
value++;
while (!IsPrime(value))
value += 2;
return(value);
}
inline int HashString(const char *string)
{
int h = 0;
while (*string != 0)
h = ((31 * h) + *string++) ^ (h >> 26);
return(h);
}
inline int UpperHashString(const char *string)
{
int h = 0;
while (*string != 0)
{
h = ((31 * h) + (*string >= 'a' && *string <= 'z') ? (*string - 0x20) : *string) ^ (h >> 26);
string++;
}
return(h);
}
} // namespace
#endif
@@ -0,0 +1,31 @@
#ifndef UDPLIBRARY_UDPHELPER_H
#define UDPLIBRARY_UDPHELPER_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
namespace UdpLibrary
{
class UdpGuard
{
public:
UdpGuard(UdpPlatformGuardObject *obj) { mObj = obj; mObj->Enter(); }
~UdpGuard() { mObj->Leave(); }
protected:
UdpPlatformGuardObject *mObj;
};
class UdpRef
{
public:
UdpRef(UdpRefCount *obj) { mObj = obj; mObj->AddRef(); }
~UdpRef() { mObj->Release(); }
private:
UdpRefCount *mObj;
};
} // namespace
#endif
@@ -0,0 +1,42 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "UdpLibrary.h"
namespace UdpLibrary
{
////////////////////////////////////////////////////////
// UdpGuardedRefCount
////////////////////////////////////////////////////////
void UdpGuardedRefCount::AddRef() const
{
UdpGuard guard(&mGuard);
UdpRefCount::AddRef();
}
void UdpGuardedRefCount::Release() const
{
mGuard.Enter();
assert(!mNoRef);
assert(mRefCount > 0);
bool done = (--mRefCount == 0);
mGuard.Leave();
if (done)
{
delete this;
}
}
} // namespace
@@ -0,0 +1,18 @@
#ifndef UDPLIBRARY_UDPLIBRARY_H
#define UDPLIBRARY_UDPLIBRARY_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <stdio.h>
#include "UdpHandler.h"
#include "UdpDriver.h"
#include "UdpHelper.h"
#include "UdpLogicalPacket.h"
#include "UdpMisc.h"
#include "UdpManager.h"
#include "UdpConnection.h"
#endif
@@ -0,0 +1,244 @@
#ifndef UDPLIBRARY_UDPLINKEDLIST_H
#define UDPLIBRARY_UDPLINKEDLIST_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
namespace UdpLibrary
{
template<typename M> class UdpLinkedListMember;
template<typename T> class UdpLinkedList;
template<typename M> class UdpLinkedListMember
{
public:
UdpLinkedListMember() { mPrev = NULL; mNext = NULL; }
UdpLinkedListMember(const UdpLinkedListMember &) { mPrev = NULL; mNext = NULL; }
~UdpLinkedListMember() {}
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
public:
#else
protected:
template<typename T> friend class UdpLinkedList;
#endif
M *mPrev;
M *mNext;
};
template<typename T> class UdpLinkedList
{
public:
UdpLinkedList(UdpLinkedListMember<T> T::*node);
virtual ~UdpLinkedList();
T *First() const;
T *Last() const;
T *Next(const T *cur) const;
T *Prev(const T *cur) const;
T *Position(int index) const;
T *Remove(T *cur);
T *RemoveHead();
T *RemoveTail();
T *InsertHead(T *cur);
T *InsertTail(T *cur);
T *InsertAfter(T *cur, T *prev);
int Count() const;
void DeleteAll(); // calls delete on all contents
void ReleaseAll(); // calls Release on all contents
protected:
T *mHead;
T *mTail;
UdpLinkedListMember<T> T::*mNode;
int mCount;
};
template<typename T> UdpLinkedList<T>::UdpLinkedList(UdpLinkedListMember<T> T::*node)
{
mHead = NULL;
mTail = NULL;
mNode = node;
mCount = 0;
}
template<typename T> UdpLinkedList<T>::~UdpLinkedList()
{
}
template<typename T> T *UdpLinkedList<T>::First() const
{
return(mHead);
}
template<typename T> T *UdpLinkedList<T>::Next(const T *cur) const
{
return((cur->*mNode).mNext);
}
template<typename T> T *UdpLinkedList<T>::Prev(const T *cur) const
{
return((cur->*mNode).mPrev);
}
template<typename T> T *UdpLinkedList<T>::Last() const
{
return(mTail);
}
template<typename T> int UdpLinkedList<T>::Count() const
{
return(mCount);
}
template<typename T> T *UdpLinkedList<T>::Position(int index) const
{
T *cur = mHead;
while (cur != NULL && index > 0)
{
cur = Next(cur);
index--;
}
return(cur);
}
template<typename T> T *UdpLinkedList<T>::Remove(T *cur)
{
UdpLinkedListMember<T> *node = &(cur->*mNode);
if (node->mPrev == NULL)
mHead = node->mNext;
else
((node->mPrev)->*mNode).mNext = node->mNext;
if (node->mNext == NULL)
mTail = node->mPrev;
else
((node->mNext)->*mNode).mPrev = node->mPrev;
node->mNext = NULL;
node->mPrev = NULL;
mCount--;
return(cur);
}
template<typename T> T *UdpLinkedList<T>::RemoveHead()
{
if (mHead == NULL)
return(NULL);
return(Remove(mHead));
}
template<typename T> T *UdpLinkedList<T>::RemoveTail()
{
if (mTail == NULL)
return(NULL);
return(Remove(mTail));
}
template<typename T> T *UdpLinkedList<T>::InsertHead(T *cur)
{
assert((cur->*mNode).mPrev == NULL);
assert((cur->*mNode).mNext == NULL);
(cur->*mNode).mNext = mHead;
if (mHead != NULL)
{
(mHead->*mNode).mPrev = cur;
mHead = cur;
}
else
{
mTail = mHead = cur;
}
mCount++;
return(cur);
}
template<typename T> T *UdpLinkedList<T>::InsertTail(T *cur)
{
assert((cur->*mNode).mPrev == NULL);
assert((cur->*mNode).mNext == NULL);
(cur->*mNode).mPrev = mTail;
if (mTail != NULL)
{
(mTail->*mNode).mNext = cur;
mTail = cur;
}
else
{
mTail = mHead = cur;
}
mCount++;
return(cur);
}
template<typename T> T *UdpLinkedList<T>::InsertAfter(T *cur, T *prev)
{
assert((cur->*mNode).mPrev == NULL);
assert((cur->*mNode).mNext == NULL);
if (prev == NULL)
return(InsertHead(cur));
(cur->*mNode).mPrev = prev;
(cur->*mNode).mNext = (prev->*mNode).mNext;
(prev->*mNode).mNext = cur;
if ((cur->*mNode).mNext != NULL)
(((cur->*mNode).mNext)->*mNode).mPrev = cur;
else
mTail = cur;
mCount++;
return(cur);
}
template<typename T> void UdpLinkedList<T>::DeleteAll()
{
T *cur = First();
while (cur != NULL)
{
T *next = Next(cur);
Remove(cur);
delete cur;
cur = next;
}
}
template<typename T> void UdpLinkedList<T>::ReleaseAll()
{
T *cur = First();
while (cur != NULL)
{
T *next = Next(cur);
Remove(cur);
cur->Release();
cur = next;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T> class UdpLinkedListOwn : public UdpLinkedList<T>
{
public:
virtual ~UdpLinkedListOwn()
{
this->DeleteAll();
}
};
} // namespace
#endif
@@ -0,0 +1,218 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <assert.h>
#include "UdpLibrary.h"
namespace UdpLibrary
{
/////////////////////////////////////////////////////
// LogicalPacket implementation
/////////////////////////////////////////////////////
LogicalPacket::LogicalPacket()
{
}
LogicalPacket::~LogicalPacket()
{
}
bool LogicalPacket::IsInternalPacket() const
{
return(false);
}
/////////////////////////////////////////////////////
// SimpleLogicalPacket implementation
/////////////////////////////////////////////////////
SimpleLogicalPacket::SimpleLogicalPacket(const void *data, int dataLen)
{
mDataLen = dataLen;
mData = new udp_uchar[mDataLen];
if (data != NULL)
memcpy(mData, data, mDataLen);
}
SimpleLogicalPacket::~SimpleLogicalPacket()
{
delete[] mData;
}
void *SimpleLogicalPacket::GetDataPtr()
{
return(mData);
}
const void *SimpleLogicalPacket::GetDataPtr() const
{
return(mData);
}
int SimpleLogicalPacket::GetDataLen() const
{
return(mDataLen);
}
void SimpleLogicalPacket::SetDataLen(int len)
{
assert(len <= mDataLen);
mDataLen = len;
}
/////////////////////////////////////////////////////
// GroupLogicalPacket implementation
/////////////////////////////////////////////////////
GroupLogicalPacket::GroupLogicalPacket() : LogicalPacket()
{
mDataLen = 0;
mData = NULL;
}
GroupLogicalPacket::~GroupLogicalPacket()
{
UdpMisc::SmartResize(mData, 0); // free it
}
void GroupLogicalPacket::AddPacket(const LogicalPacket *packet)
{
assert(packet != NULL);
AddPacketInternal(packet->GetDataPtr(), packet->GetDataLen(), packet->IsInternalPacket());
}
void GroupLogicalPacket::AddPacket(const void *data, int dataLen)
{
assert(data != NULL);
assert(dataLen >= 0);
AddPacketInternal(data, dataLen, false);
}
void GroupLogicalPacket::AddPacketInternal(const void *data, int dataLen, bool isInternalPacket)
{
if (dataLen == 0)
return;
mData = (udp_uchar *)UdpMisc::SmartResize(mData, mDataLen + dataLen + 10, 512); // 7 is the most bytes that could be needed to specify the length of the data to follow, 2 is for internal header-bytes, 1 is for zero-escape if needed (if they need to be added on)
if (mDataLen == 0)
{
mData[0] = 0;
mData[1] = UdpConnection::cUdpPacketGroup;
mDataLen = 2;
}
udp_uchar *ptr = mData + mDataLen;
if (!isInternalPacket && *(const udp_uchar *)data == 0)
{
ptr += UdpMisc::PutVariableValue(ptr, dataLen + 1);
*ptr++ = 0; // packet is not internal and starts with 0, so we need to zero-escape it so it knows it's an application packet
}
else
ptr += UdpMisc::PutVariableValue(ptr, dataLen);
memcpy(ptr, data, dataLen);
ptr += dataLen;
mDataLen = (int)(ptr - mData);
}
void *GroupLogicalPacket::GetDataPtr()
{
return(mData);
}
const void *GroupLogicalPacket::GetDataPtr() const
{
return(mData);
}
int GroupLogicalPacket::GetDataLen() const
{
return(mDataLen);
}
void GroupLogicalPacket::SetDataLen(int /*len*/)
{
assert(0); // not allowed to set the len of a group logical packet
}
bool GroupLogicalPacket::IsInternalPacket() const
{
return(true);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PooledLogicalPacket implementation
///////////////////////////////////////////////////////////////////////////////////////////
PooledLogicalPacket::PooledLogicalPacket(UdpManager *manager, int len)
{
mMaxDataLen = len;
mData = new udp_uchar[mMaxDataLen];
mDataLen = 0;
mUdpManager = manager;
mUdpManager->PoolCreated(this);
}
PooledLogicalPacket::~PooledLogicalPacket()
{
if (mUdpManager != NULL)
{
mUdpManager->PoolDestroyed(this);
mUdpManager = NULL;
}
delete[] mData;
}
void PooledLogicalPacket::AddRef() const
{
LogicalPacket::AddRef();
}
void PooledLogicalPacket::Release() const
{
if (GetRefCount() == 1 && mUdpManager != NULL)
{
// the PoolReturn function steals our reference (ie, we don't release, they don't addref), this is for thread safety reasons
mUdpManager->PoolReturn(const_cast<PooledLogicalPacket *>(this));
return; // it would be non-thread-safe for us to touch this object in any way after is is added back to the pool, since some other thread might steal it before we get back to here (or if the pool didn't want us, we are potentially deleted at this point)
}
LogicalPacket::Release();
}
void PooledLogicalPacket::TrueRelease() const
{
// this function is used by the UdpManager to delete this object in situations where it has decided that it does
// not want the object returned to the pool (pool is full)
LogicalPacket::Release();
}
void *PooledLogicalPacket::GetDataPtr()
{
return(mData);
}
const void *PooledLogicalPacket::GetDataPtr() const
{
return(mData);
}
int PooledLogicalPacket::GetDataLen() const
{
return(mDataLen);
}
void PooledLogicalPacket::SetDataLen(int len)
{
assert(len <= mMaxDataLen);
mDataLen = len;
}
void PooledLogicalPacket::SetData(const void *data, int dataLen, const void *data2, int dataLen2)
{
mDataLen = dataLen + dataLen2;
if (data != NULL)
memcpy(mData, data, dataLen);
if (data2 != NULL)
memcpy(mData + dataLen, data2, dataLen2);
}
} // namespace
@@ -0,0 +1,299 @@
#ifndef UDPLIBRARY_UDPLOGICALPACKET_H
#define UDPLIBRARY_UDPLOGICALPACKET_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include "UdpHandler.h"
#include "UdpLinkedList.h"
namespace UdpLibrary
{
class SimpleLogicalPacket;
class GroupLogicalPacket;
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The purpose of the LogicalPacket class is to provide means whereby multiple connections can share
// a queued packet to save having each connection make its own copy. It is highly recommended that
// logical packets be used for optimal performance when sending reliable data as well, since internally
// it will just end up creating one for you anyhow (LogicalPackets are used by the reliable layer to
// hold onto the data until it is acknowledged)
//
// Logical packets passed in are never modified by the udp library.
// After calling Send, application should immediately Release the logical packet if they don't need it for
// something else (like sending to another connection). The udp library will addref it if it decided it
// wants to keep it around past the Send call (for reliable data, it will always hang onto it, for unreliable
// data, it will only hang onto it if it gets promoted to reliable due to size.)
//
// Application is encouraged to derive their own application-specific packet classes from the LogicalPacket
// The object is required to be able to provide a pointer to the raw packet data that will remain valid
// for as long as the LogicalPacket object exists.
//
// for example:
// class PlayerLoginPacket : public LogicalPacket
// {
// public:
// PlayerLoginPacket(char *userName, char *password) { mData.packetType = cPacketTypePlayerLogin; strcpy(mData.userName, userName); strcpy(mData.password, password); }
// virtual const void *GetDataPtr() const { return(&mData); }
// virtual int GetDataLen() const { return(sizeof(mData)); }
// protected:
// virtual ~PlayerLoginPacket() {};
// struct
// {
// udp_uchar packetType;
// char userName[32];
// char password[32];
// } mData;
// };
////////////////////////////////////////////////////////////////////////////////////////////////////////////
class LogicalPacket : public UdpRefCount
{
public:
LogicalPacket();
virtual void *GetDataPtr() = 0;
virtual const void *GetDataPtr() const = 0;
virtual int GetDataLen() const = 0;
virtual void SetDataLen(int len) = 0;
// returns true if this logical packet is an internal packet
// by default this returns false, and applications should not override this function to do
// otherwise. Basically, if this returns true, it tells the udp library that the packet has
// an internal-packet header (starts with a zero byte) on it that should not be escaped when sent.
// The packet will ultimately be processed by the internal udp library code on the other side.
// The purpose of this is to support features such as GroupLogicalPacket, such that the application
// can prep grouped packets and then send them via traditional means (it's how the udp library
// knows that the packet is a group packet instead of just a application-packet that happens to start with 0)
virtual bool IsInternalPacket() const;
protected:
virtual ~LogicalPacket(); // protected since the class should only be deleted by Releasing it
private:
friend class UdpReliableChannel;
UdpLinkedListMember<LogicalPacket> mReliableLink; // used by reliable channel object to hold a list of these packets
};
class SimpleLogicalPacket : public LogicalPacket
{
// this class is a simply dynamically allocated packet. Applications are welcome to use it, but
// it was originally created to allow the internal code to handle reliable data that was sent
// via the Send(char *, int) api call.
public:
SimpleLogicalPacket(const void *data, int dataLen); // data can be NULL if you want to populate it after it is allocated (get the pointer and write to it)
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
protected:
virtual ~SimpleLogicalPacket();
udp_uchar *mData;
int mDataLen;
};
class WrappedLogicalPacket : public LogicalPacket
{
// this class is used internally by the library such that it can get a free and clear mNext pointer
// in cases where a logical packet is being shared by multiple connections (such that the packet
// can reside in the linked-list for the reliable channel queue)
public:
WrappedLogicalPacket(UdpManager *manager); // lives in a pool
virtual void AddRef() const;
virtual void Release() const;
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
protected:
virtual ~WrappedLogicalPacket();
const LogicalPacket *mPacket;
protected:
friend class UdpManager;
friend class UdpReliableChannel;
void TrueRelease() const;
void SetLogicalPacket(const LogicalPacket *packet);
UdpManager *mUdpManager;
UdpLinkedListMember<WrappedLogicalPacket> mAvailableLink; // for available linked list in manager (used by reliable channel object as well while it is borrowed)
UdpLinkedListMember<WrappedLogicalPacket> mCreatedLink; // for created linked list in manager
};
template<int t_quickSize> class FixedLogicalPacket : public LogicalPacket
{
// this class is similar to the SimpleLogicalPacket in that it is designed to just store raw
// data of various sizes. The difference here is that this class may be created at compile
// time to be any size desired (via template parameters), and is more efficient than the
// SimpleLogicalPacket when used (it avoid the internal alloc as the data is inline). The
// UdpMisc::CreateQuickLogicalPacket function automatically attempts to use this for small
// packets and will fall back and use SimpleLogicalPackets for larger packets.
public:
FixedLogicalPacket(const void *data, int dataLen);
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
protected:
udp_uchar mData[t_quickSize];
int mDataLen;
};
template<typename T> class StructLogicalPacket : public LogicalPacket
{
// this class is designed to turn any raw struct into a logical packet. The application
// can then access the struct through thisLogicalPacket->mStruct.structMember. The template
// will take care of all the wrappings necessary to turn the struct into a fully functional
// logical packet that can be sent to a connection. As a word of caution, sending struct's
// across the wire is not considered very portable. There are byte-ordering and structure-packing
// issues that are platform dependent. At a minimum, you will probably want to make sure that
// struct's you use in this way are packed to 1-byte boundaries, such that you are not sending
// wasteful information. Do not attempt to send the member-content of classes (particularly classes
// with virtual functions) via this method as they may contain hidden data-members (such as pointers
// to vtables).
public:
StructLogicalPacket(T *initData = NULL);
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
protected:
T mStruct;
};
class GroupLogicalPacket : public LogicalPacket
{
// this class is a helper object intended for use by the application (it is not used internally)
// It allows you to add multiple application-packets and them send them all as an autonomous unit.
// The receiving end will automatically split the packet up and send them to the application callback function
// as if the individual packets had all been sent one at at time.
//
// This facility is primarily intended for reliable data, though it can be used to group unreliable data as well.
// Grouping unreliable data together would effectively give you an all-or-nothing type of delivery system. Be
// mindful that if the group of packets gets larger than max-raw-packet-size, it will end up getting
// promoted to being a reliable-packet with the associated overhead involved in that.
//
// You might be wondering what the advantage would be of grouping packets together at the application level as opposed to
// just letting the internal multi-buffer take care of the problem for you. For starters, the internal multi-buffer
// is incapable of combining partial-packets, so you end up sending less than maximum-size packets if you let the
// internal layer take care of it. Additionally, there is additional overhead in that if you send 100 tiny logical
// packets, each one will need to be ack'ed by the receiving end (even though they are getting combined down at the physical-packet
// level in order to reduce UDP overhead). Finally, grouping them together at the higher level will allow the
// logical-packet compression helper routines to operate on larger chunks of data at a time, which tends to improve
// compression efficiency. The downside to combining is that none of the application packets get delivered until the
// entire group arrives, then all are delivered in one fell swoop...the net effect being that in order to gain these efficeincies,
// the first-packet takes longer to be delivered to the application than it otherwise would have (while the last packet will in theory
// get there is less time as there will be less overhead).
//
// Internal packets can be added to the group (though that is somewhat unlikely), which means you can add a group to a group
public:
GroupLogicalPacket();
void AddPacket(const LogicalPacket *packet); // cannot add internal logical packets to the group
void AddPacket(const void *data, int dataLen);
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
virtual bool IsInternalPacket() const; // returns true if this is considering an internal logical packet type
protected:
virtual ~GroupLogicalPacket();
void AddPacketInternal(const void *data, int dataLen, bool isInternalPacket);
udp_uchar *mData;
int mDataLen;
};
class PooledLogicalPacket : public LogicalPacket
{
// a pooled logical packet is like other logical packets, only when it's refCount gets down to 1, it notifies
// its manager to add it back to the pool. (The manager keeps the last ref count on the packet)
public:
PooledLogicalPacket(UdpManager *manager, int len);
virtual void AddRef() const;
virtual void Release() const;
virtual void *GetDataPtr();
virtual const void *GetDataPtr() const;
virtual int GetDataLen() const;
virtual void SetDataLen(int len);
protected:
virtual ~PooledLogicalPacket();
udp_uchar *mData;
int mDataLen;
int mMaxDataLen;
protected:
friend class UdpManager;
void TrueRelease() const;
void SetData(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0);
UdpManager *mUdpManager;
UdpLinkedListMember<PooledLogicalPacket> mAvailableLink; // for available linked list in manager
UdpLinkedListMember<PooledLogicalPacket> mCreatedLink; // for created linked list in manager
};
/////////////////////////////////////////////////////////////////////////
// FixedLogicalPacket implementation
/////////////////////////////////////////////////////////////////////////
template<int t_quickSize> FixedLogicalPacket<t_quickSize>::FixedLogicalPacket(const void *data, int dataLen)
{
mDataLen = dataLen;
if (data != NULL)
memcpy(mData, data, mDataLen);
}
template<int t_quickSize> void *FixedLogicalPacket<t_quickSize>::GetDataPtr()
{
return(mData);
}
template<int t_quickSize> const void *FixedLogicalPacket<t_quickSize>::GetDataPtr() const
{
return(mData);
}
template<int t_quickSize> int FixedLogicalPacket<t_quickSize>::GetDataLen() const
{
return(mDataLen);
}
template<int t_quickSize> void FixedLogicalPacket<t_quickSize>::SetDataLen(int len)
{
mDataLen = len;
}
/////////////////////////////////////////////////////////////////////////
// StructLogicalPacket implementation
/////////////////////////////////////////////////////////////////////////
template<typename T> StructLogicalPacket<T>::StructLogicalPacket(T *initData)
{
if (initData != NULL)
mStruct = *initData;
}
template<typename T> void *StructLogicalPacket<T>::GetDataPtr()
{
return(&mStruct);
}
template<typename T> const void *StructLogicalPacket<T>::GetDataPtr() const
{
return(&mStruct);
}
template<typename T> int StructLogicalPacket<T>::GetDataLen() const
{
return(sizeof(mStruct));
}
template<typename T> void StructLogicalPacket<T>::SetDataLen(int len)
{
}
} // namespace
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,253 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <string.h>
#include "UdpMisc.h"
#include "UdpLogicalPacket.h"
namespace UdpLibrary
{
////////////////////////////////////////////////////////////////////////////////////////////////////
// UdpMisc functions
////////////////////////////////////////////////////////////////////////////////////////////////////
int UdpMisc::SyncStampShortDeltaTime(udp_ushort stamp1, udp_ushort stamp2)
{
udp_ushort delta = (udp_ushort)(stamp1 - stamp2);
if (delta > 0x7fff)
return((int)(0xffff - delta));
return((int)delta);
}
int UdpMisc::SyncStampLongDeltaTime(udp_uint stamp1, udp_uint stamp2)
{
udp_uint delta = stamp1 - stamp2;
if (delta > 0x7fffffff)
return((int)(0xffffffff - delta));
return((int)delta);
}
int UdpMisc::Random(int *seed)
{
int hi = *seed / 127773;
int lo = *seed % 127773;
int t = lo * 16807 - hi * 2836 + 123;
*seed = (t > 0) ? t : (t + 2147483647);
return(*seed);
}
int UdpMisc::Crc32(const void *buffer, int bufferLen, int encryptValue)
{
static unsigned crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D};
int crc = 0xffffffff;
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ (encryptValue & 0xff)) & 0x000000FFL];
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 8) & 0xff)) & 0x000000FFL];
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 16) & 0xff)) & 0x000000FFL];
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ ((encryptValue >> 24) & 0xff)) & 0x000000FFL];
const udp_uchar *bufPtr = (const udp_uchar *)buffer;
const udp_uchar *endPtr = (const udp_uchar *)buffer + bufferLen;
while (bufPtr < endPtr)
{
crc = ((crc >> 8) & 0x00FFFFFFL) ^ crc32_table[(crc ^ *bufPtr) & 0x000000FFL];
bufPtr++;
}
crc = ~crc;
return(crc);
}
void *UdpMisc::SmartResize(void *ptr, int bytes, int round)
{
enum { cAlignment = sizeof(int) };
// rounding stuff for speed
bytes = ((bytes + round - 1) / round) * round;
if (bytes == 0)
{
if (ptr != NULL)
{
free((udp_uchar *)ptr - cAlignment);
}
return(NULL);
}
udp_uchar *ptr2;
if (ptr == NULL)
{
ptr2 = (udp_uchar *)malloc(bytes + cAlignment);
if (ptr2 == NULL)
return(NULL);
*(int *)ptr2 = bytes;
return(ptr2 + cAlignment);
}
int oldBytes = *((int *)((udp_uchar *)ptr - cAlignment));
if (bytes == oldBytes)
return(ptr);
ptr2 = (udp_uchar *)realloc((udp_uchar *)ptr - cAlignment, bytes + cAlignment);
if (ptr2 == NULL)
return(NULL);
*(int *)ptr2 = bytes;
return(ptr2 + cAlignment);
}
udp_uint UdpMisc::PutVariableValue(void *buffer, udp_uint value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
udp_uint store = (udp_uint)value;
if (store < 254)
{
*bufptr = (udp_uchar)store;
return(1);
}
else if (store < 0xffff)
{
*bufptr = 0xff;
*(bufptr + 1) = (udp_uchar)(store >> 8);
*(bufptr + 2) = (udp_uchar)(store & 0xff);
return(3);
}
else
{
*bufptr = 0xff;
*(bufptr + 1) = 0xff;
*(bufptr + 2) = 0xff;
*(bufptr + 3) = (udp_uchar)(store >> 24);
*(bufptr + 4) = (udp_uchar)((store >> 16) & 0xff);
*(bufptr + 5) = (udp_uchar)((store >> 8) & 0xff);
*(bufptr + 6) = (udp_uchar)(store & 0xff);
return(7);
}
}
udp_uint UdpMisc::GetVariableValue(const void *buffer, udp_uint *value)
{
const udp_uchar *bufptr = (const udp_uchar *)buffer;
if (*bufptr == 0xff)
{
if (*(bufptr + 1) == 0xff && *(bufptr + 2) == 0xff)
{
*value = (udp_uint)((*(bufptr + 3) << 24) | (*(bufptr + 4) << 16) | (*(bufptr + 5) << 8) | *(bufptr + 6));
return(7);
}
*value = (udp_uint)((*(bufptr + 1) << 8) | *(bufptr + 2));
return(3);
}
*value = (udp_uint)*bufptr;
return(1);
}
LogicalPacket *UdpMisc::CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2, int dataLen2)
{
enum { cQuickFactor = 128 };
int totalDataLen = dataLen + dataLen2;
int q = (totalDataLen - 1) / cQuickFactor;
LogicalPacket *tlp;
switch(q)
{
case 0:
tlp = new FixedLogicalPacket<cQuickFactor>(NULL, totalDataLen);
break;
case 1:
tlp = new FixedLogicalPacket<cQuickFactor * 2>(NULL, totalDataLen);
break;
case 2:
case 3:
tlp = new FixedLogicalPacket<cQuickFactor * 4>(NULL, totalDataLen);
break;
case 4:
case 5:
case 6:
case 7:
tlp = new FixedLogicalPacket<cQuickFactor * 8>(NULL, totalDataLen);
break;
default:
tlp = new SimpleLogicalPacket(NULL, totalDataLen);
break;
}
udp_uchar *dest = (udp_uchar *)tlp->GetDataPtr();
if (data != NULL)
memcpy(dest, data, dataLen);
if (data2 != NULL)
memcpy(dest + dataLen, data2, dataLen2);
return(tlp);
}
UdpPlatformAddress UdpMisc::GetHostByName(const char *hostName)
{
static UdpPlatformDriver sDriver;
UdpPlatformAddress ip;
sDriver.GetHostByName(&ip, hostName);
return(ip);
}
void UdpMisc::Sleep(int milliseconds)
{
static UdpPlatformDriver sDriver;
sDriver.Sleep(milliseconds);
}
UdpPlatformAddress UdpMisc::GetSelfAddress(bool preferNoRoute)
{
static UdpPlatformDriver sDriver;
UdpPlatformAddress ip;
sDriver.GetSelfAddress(&ip, preferNoRoute);
return(ip);
}
} // namespace
@@ -0,0 +1,237 @@
#ifndef UDPLIBRARY_UDPMISC_H
#define UDPLIBRARY_UDPMISC_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <stdlib.h>
#include <string.h>
#include "UdpDriver.h"
namespace UdpLibrary
{
class LogicalPacket;
class UdpMisc
{
////////////////////////////////////////////////////////////////////////////////////////////
// This is a group of miscellaneous function used as part of the implementation
// The application is free to use these helper functions as well if finds them useful.
// they are stuck into this class so as to avoid conflicts with the application
////////////////////////////////////////////////////////////////////////////////////////////
public:
#if 1 // DEPRECATED (here for backwards compatibility only. Not used by UdpLibrary, it uses the UdpPlatformDriver version)
// Internally these create a static UdpPlatformDriver and chain the call through. If you have a UdpManager, I recommend
// simply call UdpManager::Clock and UdpManager::ClockElapsed, which chain on through to the driver created by the UdpManager
// values returned by this clock function should not be compared to values return from the UdpManager clock function as
// they go through two different drivers to get the answer and may not return the same number (though in practice it
// likely always will).
typedef UdpClockStamp ClockStamp;
static ClockStamp Clock(); // returns a timestamp (most likely in milliseconds)
static int ClockElapsed(ClockStamp stamp); // returns a elapsed time since stamp in milliseconds (if elapsed is over 23 days, it returns 23 days)
#endif
static int ClockDiff(UdpClockStamp start, UdpClockStamp stop); // returns a time difference in milliseconds (if difference is over 23 days, it returns 23 days)
static int Crc32(const void *buffer, int bufferLen, int encryptValue = 0); // calculate a 32-bit crc for a buffer (encrypt value simple scrambles the crc at the beginning so the same packet doesn't produce the same crc on different connections)
static int Random(int *seed); // random number generator
static void Sleep(int milliseconds);
// returns the time difference between two sync-stamps that are based on the same clock
// for example, two LocalSyncStampShort stamps can be compared. Only differences up to
// 32 seconds can be timed. There is also a UdpConnect::ServerSyncStampShort function that returns
// you a sync-stamp that you can compare with ServerSyncStampShort values generated on other
// machines that are synchronized against the same server. This ServerSyncStampShort function and
// this delta time function serve as the basis for calculating one-way travel times for packets.
static int SyncStampShortDeltaTime(udp_ushort stamp1, udp_ushort stamp2);
static int SyncStampLongDeltaTime(udp_uint stamp1, udp_uint stamp2); // same as Short version only no limit
// used to alloc/resize/free an allocation previously created with this function
// rounding causes that it pre-allocates additional space up to the rounded buffer size
// this allows the function to be called over and over again as tiny members are added, yet
// only do an actual realloc periodically. A 4-byte header is invisibly prepended on the
// allocated block such that it can keep track of the actual size of the block. The built-in
// memory manager does a little bit of this, but if you know you have an allocation that is likely
// to grow quite a bit, you can set the round'ing size up to a fairly large number and avoid
// unnecessary reallocs at the cost of a little potentially wasted space
// initial allocations are done by passing in ptr==NULL, freeing is done by passing in bytes==0
static void *SmartResize(void *ptr, int bytes, int round = 1);
// the following two functions store values in the buffer as a variable length (B1, 0xffB2B1, 0xffffffB4B3B2B1)
// such that values under 254 take one byte, values under 65535 take three bytes, and larger values take seven bytes
static udp_uint PutVariableValue(void *buffer, udp_uint value); // returns the number of bytes it took to store the value in the buffer
static udp_uint GetVariableValue(const void *buffer, udp_uint *value); // returns the number of bytes it took to get the value from the buffer
// these functions are used to aid in portability and serve to ensure that the packet-headers are interpretted in the same
// manner on all platforms
static int PutValue64(void *buffer, udp_int64 value); // puts a 64-bit value into the buffer in big-endian format, returns number of bytes used(8)
static int PutValue32(void *buffer, udp_uint value); // puts a 32-bit value into the buffer in big-endian format, returns number of bytes used(4)
static int PutValue24(void *buffer, udp_uint value); // puts a 24-bit value into the buffer in big-endian format, returns number of bytes used(4)
static int PutValue16(void *buffer, udp_ushort value); // puts a 16-bit value into the buffer in big-endian format, returns number of bytes used(2)
static int PutValueLE32(void *buffer, udp_uint value); // puts a 32-bit value in little-endian format
static udp_int64 GetValue64(const void *buffer); // gets a 64-bit value from the buffer in big-endian format
static udp_uint GetValue32(const void *buffer); // gets a 32-bit value from the buffer in big-endian format
static udp_uint GetValue24(const void *buffer); // gets a 24-bit value from the buffer in big-endian format
static udp_ushort GetValue16(const void *buffer); // gets a 16-bit value from the buffer in big-endian format
static LogicalPacket *CreateQuickLogicalPacket(const void *data, int dataLen, const void *data2 = NULL, int dataLen2 = 0);
// looks up the specified name and translates it to an IP address
// this is a blocking call that can at times take a significant amount of time, but will generally be fast (less than 300ms)
static UdpPlatformAddress GetHostByName(const char *hostName);
static UdpPlatformAddress GetSelfAddress(bool preferNoRoute);
static char *Strncpy(char *dest, const char *source, int len);
static char *Strncat(char *dest, const char *source, int len);
};
////////////////////////////////////////////////////////////////////////////
// inline implementations
////////////////////////////////////////////////////////////////////////////
// UdpMisc
#if 1 // DEPRECATED (see declaration)
inline UdpMisc::ClockStamp UdpMisc::Clock()
{
static UdpPlatformDriver sClockDriver;
return(sClockDriver.Clock());
}
inline int UdpMisc::ClockElapsed(ClockStamp stamp)
{
return(UdpMisc::ClockDiff(stamp, Clock()));
}
#endif
inline int UdpMisc::ClockDiff(UdpClockStamp start, UdpClockStamp stop)
{
UdpClockStamp t = (stop - start);
if (t > 0x7fffffff) // only time differences up to about 23 days can be measured with this function
return(0x7fffffff);
return((int)t);
}
inline int UdpMisc::PutValue64(void *buffer, udp_int64 value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
*bufptr++ = (udp_uchar)(value >> 56);
*bufptr++ = (udp_uchar)((value >> 48) & 0xff);
*bufptr++ = (udp_uchar)((value >> 40) & 0xff);
*bufptr++ = (udp_uchar)((value >> 32) & 0xff);
*bufptr++ = (udp_uchar)((value >> 24) & 0xff);
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
*bufptr = (udp_uchar)(value & 0xff);
return(8);
}
inline udp_int64 UdpMisc::GetValue64(const void *buffer)
{
const udp_uchar *bufptr = (const udp_uchar *)buffer;
return(((udp_int64)*bufptr << 56) | ((udp_int64)*(bufptr + 1) << 48) | ((udp_int64)*(bufptr + 2) << 40) | ((udp_int64)*(bufptr + 3) << 32) | ((udp_int64)*(bufptr + 4) << 24) | ((udp_int64)*(bufptr + 5) << 16) | ((udp_int64)*(bufptr + 6) << 8) | (udp_int64)*(bufptr + 7));
}
inline int UdpMisc::PutValue32(void *buffer, udp_uint value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
*bufptr++ = (udp_uchar)(value >> 24);
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
*bufptr = (udp_uchar)(value & 0xff);
return(4);
}
inline int UdpMisc::PutValueLE32(void *buffer, udp_uint value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
*bufptr++ = (udp_uchar)(value & 0xff);
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
*bufptr = (udp_uchar)(value >> 24);
return(4);
}
inline udp_uint UdpMisc::GetValue32(const void *buffer)
{
const udp_uchar *bufptr = (const udp_uchar *)buffer;
return((*bufptr << 24) | (*(bufptr + 1) << 16) | (*(bufptr + 2) << 8) | *(bufptr + 3));
}
inline int UdpMisc::PutValue24(void *buffer, udp_uint value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
*bufptr++ = (udp_uchar)((value >> 16) & 0xff);
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
*bufptr = (udp_uchar)(value & 0xff);
return(3);
}
inline udp_uint UdpMisc::GetValue24(const void *buffer)
{
const udp_uchar *bufptr = (const udp_uchar *)buffer;
return((*bufptr << 16) | (*(bufptr + 1) << 8) | *(bufptr + 2));
}
inline int UdpMisc::PutValue16(void *buffer, udp_ushort value)
{
udp_uchar *bufptr = (udp_uchar *)buffer;
*bufptr++ = (udp_uchar)((value >> 8) & 0xff);
*bufptr = (udp_uchar)(value & 0xff);
return(2);
}
inline udp_ushort UdpMisc::GetValue16(const void *buffer)
{
const udp_uchar *bufptr = (const udp_uchar *)buffer;
return((udp_ushort)((*bufptr << 8) | *(bufptr + 1)));
}
inline char *UdpMisc::Strncpy(char *dest, const char *source, int len)
{
if (len > 0)
{
char *walk = dest;
char *end = dest + len - 1;
while (*source != 0 && walk < end)
{
*walk++ = *source++;
}
*walk = 0;
}
return(dest);
}
inline char *UdpMisc::Strncat(char *dest, const char *source, int len)
{
int clen = (int)strlen(dest);
len -= clen;
if (len > 1)
{
Strncpy(dest + clen, source, len);
}
return(dest);
}
template <typename ValueType> const ValueType &udpMax(const ValueType &a, const ValueType &b)
{
if (a < b)
return b;
return a;
}
template<typename ValueType> const ValueType &udpMin(const ValueType &a, const ValueType &b)
{
if (b < a)
return b;
return a;
}
} // namespace
#endif
@@ -0,0 +1,233 @@
#ifndef UDPLIBRARY_UDPPRIORITY_H
#define UDPLIBRARY_UDPPRIORITY_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
namespace UdpLibrary
{
template<typename T, typename P> class PriorityQueue;
// classes that wish to be members of the priority queue below must derive themselves from this class
// in order to pull in the two member variables. Unlike normal priority queue classes which don't
// have this restriction, it is required in order to support Remove and reprioritize functionality
// in a timely manner (otherwise, in order to remove an entry that is not at the top, you would have
// to linearly scan the entire queue). This is accomplished by having the member itself contain
// a pointer to it's position in the queue (mPriorityQueuePosition).
// note: a restriction of this ability is that an object cannot participate in more than one priority
// queue at a time.
class PriorityQueueMember
{
public:
PriorityQueueMember();
#if defined(_MSC_VER) && (_MSC_VER < 1300) // MSVC 7.0 is the first version to support friend templates
public:
#else
protected:
template<typename T, typename P> friend class PriorityQueue;
#endif
int mPriorityQueuePosition; // -1=not in queue
};
// this class provides a priority queue that is capable of reprioritizing/removing entries. The
// compiler will ensure that objects stored in this class are derived from PriorityQueueMember.
// We don't really need this to be a template class, since we could just treat everything as a
// PriorityQueueMember, but then the application would lose some type checking. We don't use references
// in the api as most priority queue templates do as we can't support non pointer types anyways.
template<typename T, typename P> class PriorityQueue
{
public:
PriorityQueue(int queueSize);
~PriorityQueue();
T* Top(); // returns NULL if queue is empty
T* TopRemove(); // returns NULL if queue is empty
T* TopRemove(P priority); // removes item from queue if it has a lower priority value, otherwise return NULL
T* Add(T* entry, P priority); // reprioritizes if already in queue, returns entry always
T* Remove(T* entry); // returns entry always (even if it was not in the queue)
P *GetPriority(T* entry); // returns NULL if entry is not in the queue
int QueueUsed(); // returns how many entries are in the queue
protected:
struct QueueEntry
{
T* entry;
P priority;
};
QueueEntry* mQueue;
int mQueueSize;
int mQueueEnd;
void Refloat(T* entry);
};
//////////////////////////////////////////////////////////////////////////
// PriorityQueueMember implementation
//////////////////////////////////////////////////////////////////////////
inline PriorityQueueMember::PriorityQueueMember()
{
mPriorityQueuePosition = -1;
}
//////////////////////////////////////////////////////////////////////////
// PriorityQueue implementation
//////////////////////////////////////////////////////////////////////////
template<typename T, typename P> PriorityQueue<T, P>::PriorityQueue(int queueSize)
{
mQueueEnd = 0;
mQueueSize = queueSize;
mQueue = new QueueEntry[mQueueSize];
memset(mQueue, 0, sizeof(mQueue));
}
template<typename T, typename P> PriorityQueue<T, P>::~PriorityQueue()
{
delete[] mQueue;
}
template<typename T, typename P> T* PriorityQueue<T, P>::Top()
{
if (mQueueEnd == 0)
return(NULL);
return(mQueue[0].entry);
}
template<typename T, typename P> T* PriorityQueue<T, P>::TopRemove()
{
if (mQueueEnd == 0)
return(NULL);
T* top = mQueue[0].entry;
Remove(top);
return(top);
}
template<typename T, typename P> T* PriorityQueue<T, P>::TopRemove(P priority)
{
if (mQueueEnd > 0 && mQueue[0].priority <= priority)
return(Remove(mQueue[0].entry));
return(NULL);
}
template<typename T, typename P> P* PriorityQueue<T, P>::GetPriority(T* entry)
{
if (entry->mPriorityQueuePosition >= 0)
return(&mQueue[entry->mPriorityQueuePosition].priority);
return(NULL);
}
template<typename T, typename P> T* PriorityQueue<T, P>::Add(T* entry, P priority)
{
if (entry->mPriorityQueuePosition == -1)
{
// not in queue, so add it to the bottom
if (mQueueEnd >= mQueueSize)
return(NULL);
mQueue[mQueueEnd].entry = entry;
mQueue[mQueueEnd].priority = priority;
mQueue[mQueueEnd].entry->mPriorityQueuePosition = mQueueEnd;
mQueueEnd++;
}
else
{
// see if priority has actually changed, if not, just return, otherwise change priority and fall through to refloat it
if (mQueue[entry->mPriorityQueuePosition].priority == priority)
return(entry);
mQueue[entry->mPriorityQueuePosition].priority = priority;
}
Refloat(entry);
return(entry);
}
template<typename T, typename P> T* PriorityQueue<T, P>::Remove(T* entry)
{
if (entry->mPriorityQueuePosition == -1)
return(entry);
// move end entry into place of one being removed
mQueueEnd--;
int spot = entry->mPriorityQueuePosition;
if (spot != mQueueEnd) // don't remove last item in queue (bottom of tree), so no need to copy bottom one up and refloat it (we would be refloating our own removed entry)
{
mQueue[spot] = mQueue[mQueueEnd];
mQueue[spot].entry->mPriorityQueuePosition = spot;
Refloat(mQueue[spot].entry);
}
entry->mPriorityQueuePosition = -1;
return(entry);
}
template<typename T, typename P> void PriorityQueue<T, P>::Refloat(T* entry)
{
// float upward
int spot = entry->mPriorityQueuePosition;
bool tryDown = true;
while (spot > 0 && mQueue[spot].priority < mQueue[(spot - 1) / 2].priority)
{
int newSpot = (spot - 1) / 2;
QueueEntry hold = mQueue[spot];
mQueue[spot] = mQueue[newSpot];
mQueue[newSpot] = hold;
mQueue[spot].entry->mPriorityQueuePosition = spot;
mQueue[newSpot].entry->mPriorityQueuePosition = newSpot;
spot = newSpot;
tryDown = false;
}
if (tryDown)
{
// if we didn't manage to float up at all, then we need to try floating down
for (;;)
{
// pick smallest child
int downSpot1 = (spot * 2) + 1;
if (downSpot1 >= mQueueEnd)
break;
int downSpot2 = (spot * 2) + 2;
if (downSpot2 >= mQueueEnd || mQueue[downSpot1].priority < mQueue[downSpot2].priority)
{
if (mQueue[downSpot1].priority < mQueue[spot].priority)
{
QueueEntry hold = mQueue[spot];
mQueue[spot] = mQueue[downSpot1];
mQueue[downSpot1] = hold;
mQueue[spot].entry->mPriorityQueuePosition = spot;
mQueue[downSpot1].entry->mPriorityQueuePosition = downSpot1;
spot = downSpot1;
}
else
break;
}
else
{
if (mQueue[downSpot2].priority < mQueue[spot].priority)
{
QueueEntry hold = mQueue[spot];
mQueue[spot] = mQueue[downSpot2];
mQueue[downSpot2] = hold;
mQueue[spot].entry->mPriorityQueuePosition = spot;
mQueue[downSpot2].entry->mPriorityQueuePosition = downSpot2;
spot = downSpot2;
}
else
break;
}
}
}
}
template<typename T, typename P> int PriorityQueue<T, P>::QueueUsed()
{
return(mQueueEnd);
}
} // namespace
#endif
@@ -0,0 +1,881 @@
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "UdpLibrary.h"
#include "UdpReliableChannel.h"
namespace UdpLibrary
{
/////////////////////////////////////////////////////////////////////////////////////////////////////
// ReliableChannel implementation
/////////////////////////////////////////////////////////////////////////////////////////////////////
UdpReliableChannel::UdpReliableChannel(int channelNumber, UdpConnection *con, UdpReliableConfig *config) : mLogicalPacketList(&LogicalPacket::mReliableLink)
{
mUdpConnection = con;
mChannelNumber = channelNumber;
mConfig = *config;
mConfig.maxOutstandingPackets = udpMin(mConfig.maxOutstandingPackets, (int)UdpManager::cHardMaxOutstandingPackets);
mAveragePingTime = 800; // start out fairly high so we don't do a lot of resends on a bad connection to start out with
mTrickleLastSend = 0;
int fragmentSize = mConfig.fragmentSize;
if (fragmentSize == 0 || fragmentSize > mUdpConnection->mConnectionConfig.maxRawPacketSize)
fragmentSize = mUdpConnection->mConnectionConfig.maxRawPacketSize;
mMaxDataBytes = (fragmentSize - UdpConnection::cUdpPacketReliableSize - mUdpConnection->mConnectionConfig.crcBytes - mUdpConnection->mEncryptExpansionBytes);
assert(mMaxDataBytes > 0); // fragment size/max-raw-packet-size set too small to allow for reliable deliver
if (mConfig.trickleSize != 0)
mMaxDataBytes = udpMin(mMaxDataBytes, mConfig.trickleSize);
mMaxCoalesceAttemptBytes = -1;
if (mConfig.coalesce)
{
mMaxCoalesceAttemptBytes = mMaxDataBytes - 5; // 2 bytes for group-header, 3 bytes for length of packet
}
mReliableIncomingId = 0;
mReliableOutgoingId = 0;
mReliableOutgoingPendingId = 0;
mReliableOutgoingBytes = 0;
mLogicalBytesQueued = 0;
mCoalescePacket = NULL;
mCoalesceStartPtr = NULL;
mCoalesceEndPtr = NULL;
mCoalesceCount = 0;
mBufferedAckPtr = NULL;
mStatDuplicatePacketsReceived = 0;
mStatResentPacketsAccelerated = 0;
mStatResentPacketsTimedOut = 0;
mWindowResetTime = 0;
mCongestionWindowMinimum = udpMax(mMaxDataBytes, mConfig.congestionWindowMinimum);
mCongestionWindowStart = udpMin(4 * mMaxDataBytes, udpMax(2 * mMaxDataBytes, 4380));
mCongestionWindowStart = udpMax(mCongestionWindowStart, mCongestionWindowMinimum);
mCongestionSlowStartThreshhold = udpMin(mConfig.maxOutstandingPackets * mMaxDataBytes, mConfig.maxOutstandingBytes);
mCongestionWindowSize = mCongestionWindowStart;
mBigDataLen = 0;
mBigDataTargetLen = 0;
mBigDataPtr = NULL;
mFragmentNextPos = 0;
mLastTimeStampAcknowledged = 0;
mMaxxedOutCurrentWindow = false;
mNextNeedTime = 0;
mPhysicalPackets = new PhysicalPacket[mConfig.maxOutstandingPackets];
mReliableIncoming = new IncomingQueueEntry[mConfig.maxInstandingPackets];
}
UdpReliableChannel::~UdpReliableChannel()
{
if (mCoalescePacket != NULL)
{
mCoalescePacket->Release();
mCoalescePacket = NULL;
}
const LogicalPacket *cur = mLogicalPacketList.RemoveHead();
while (cur != NULL)
{
cur->Release();
cur = mLogicalPacketList.RemoveHead();
}
delete[] mPhysicalPackets; // destructor will release any logical packets as appropriate
delete[] mReliableIncoming; // destructor will delete any data as appropriate
// delete any big packet that might be under construction
delete[] mBigDataPtr;
}
void UdpReliableChannel::Send(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2)
{
if (mLogicalPacketList.Count() == 0 && mCoalescePacket == NULL)
{
// if we are adding something to a previously empty logical queue, then it is possible that
// we may be able to send it, so mark ourselves to take time the next time it is offered
mNextNeedTime = 0;
mUdpConnection->ScheduleTimeNow();
}
if (dataLen + dataLen2 <= mMaxCoalesceAttemptBytes)
{
SendCoalesce(data, dataLen, data2, dataLen2);
}
else
{
FlushCoalesce();
LogicalPacket *packet = mUdpConnection->mUdpManager->CreatePacket(data, dataLen, data2, dataLen2);
QueueLogicalPacket(packet);
packet->Release();
}
}
void UdpReliableChannel::FlushCoalesce()
{
if (mCoalescePacket != NULL)
{
if (mCoalesceCount == 1)
{
// only one packet in coalesce, so remove the coalesce/group header and make it just a simple raw packet
int firstLen;
int skipLen = UdpMisc::GetVariableValue(mCoalesceStartPtr + 2, (udp_uint *)&firstLen);
memmove(mCoalesceStartPtr, mCoalesceStartPtr + 2 + skipLen, firstLen);
mCoalesceEndPtr = mCoalesceStartPtr + firstLen;
}
mCoalescePacket->SetDataLen((int)(mCoalesceEndPtr - mCoalesceStartPtr));
QueueLogicalPacket(mCoalescePacket);
mCoalescePacket->Release();
mCoalescePacket = NULL;
}
}
void UdpReliableChannel::SendCoalesce(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2)
{
int totalLen = dataLen + dataLen2;
if (mCoalescePacket == NULL)
{
mCoalescePacket = mUdpConnection->mUdpManager->CreatePacket(NULL, mMaxDataBytes);
mCoalesceEndPtr = mCoalesceStartPtr = (udp_uchar *)mCoalescePacket->GetDataPtr();
*mCoalesceEndPtr++ = 0;
*mCoalesceEndPtr++ = UdpConnection::cUdpPacketGroup;
mCoalesceCount = 0;
}
else
{
int spaceLeft = mMaxDataBytes - (int)(mCoalesceEndPtr - mCoalesceStartPtr);
if (totalLen + 3 > spaceLeft) // 3 bytes to ensure PutVariableValue has room for the length indicator (this limits us to 64k coalescing, which is ok, since fragments can't get that big)
{
FlushCoalesce();
SendCoalesce(data, dataLen, data2, dataLen2);
return;
}
}
// append on end of coalesce
mCoalesceCount++;
mCoalesceEndPtr += UdpMisc::PutVariableValue(mCoalesceEndPtr, totalLen);
if (data != NULL)
memcpy(mCoalesceEndPtr, data, dataLen);
mCoalesceEndPtr += dataLen;
if (data2 != NULL)
memcpy(mCoalesceEndPtr, data2, dataLen2);
mCoalesceEndPtr += dataLen2;
}
void UdpReliableChannel::QueueLogicalPacket(LogicalPacket *packet)
{
packet->AddRef();
mLogicalBytesQueued += packet->GetDataLen();
mLogicalPacketList.InsertTail(packet);
}
bool UdpReliableChannel::PullDown(int windowSpaceLeft)
{
// the pull-down on-demand method will give us the opportunity to late-combine as many tiny logical packets as we can,
// reducing the number of tracked-packets that are required. This effectively reduces the number of acks that we are
// going to get back, which can be substantial in situations where there are a LOT of tiny reliable packets being sent.
// operating with fewer outstanding physical-packets to track is more CPU efficient as well.
// (NOTE: as of this writing, the below implementation does not do the late-combine techique yet)
// (NOTE: we currently combine on send instead, which generally accomplishes the same thing)
bool pulledDown = false;
int physicalCount = (int)(mReliableOutgoingId - mReliableOutgoingPendingId);
while (windowSpaceLeft > 0 && physicalCount < mConfig.maxOutstandingPackets)
{
if (mLogicalPacketList.Count() == 0)
{
FlushCoalesce(); // this is guaranteed to stick
if (mLogicalPacketList.Count() == 0)
break; // nothing flushed, so we are done
}
int nextSpot = (int)(mReliableOutgoingId % mConfig.maxOutstandingPackets);
// ok, we can move something down, even if it is only a fragment of the logical packet
PhysicalPacket *entry = &mPhysicalPackets[nextSpot];
entry->mParent = mLogicalPacketList.First();
entry->mParent->AddRef(); // add ref from physical packet
entry->mFirstTimeStamp = 0;
entry->mLastTimeStamp = 0;
// calculate how much we can send based on our starting position (mFragmentNextPos) in the logical packet.
// if we can't send it the rest of data to end of packet, then send the fragment portion and addref, otherwise send the whole thing and pop the logical packet
int dataLen = entry->mParent->GetDataLen();
const udp_uchar *data = (const udp_uchar *)entry->mParent->GetDataPtr();
int bytesLeft = dataLen - mFragmentNextPos;
int bytesToSend = udpMin(bytesLeft, mMaxDataBytes);
entry->mDataPtr = data + mFragmentNextPos;
// if not sending entire packet
if (bytesToSend != dataLen)
{
// mark it as a fragment
if (mFragmentNextPos == 0)
bytesToSend -= 4; // fragment start has a 4 byte header specifying size of following large data, so make room for it so we don't exceed max raw packet size
}
entry->mDataLen = bytesToSend;
mReliableOutgoingBytes += bytesToSend;
if (bytesToSend == bytesLeft)
{
mFragmentNextPos = 0;
const LogicalPacket *lp = mLogicalPacketList.RemoveHead();
lp->Release(); // release from logical queue
}
else
{
mFragmentNextPos += bytesToSend;
}
mLogicalBytesQueued -= bytesToSend; // as fragments are sent, decrease the number of logical bytes queued
mReliableOutgoingId++;
physicalCount++;
windowSpaceLeft -= bytesToSend;
pulledDown = true;
}
return(pulledDown);
}
int UdpReliableChannel::GiveTime()
{
udp_uchar buf[256];
udp_uchar *bufPtr;
UdpClockStamp hotClock = mUdpConnection->GetUdpManager()->CachedClock();
if (hotClock < mNextNeedTime)
return(UdpMisc::ClockDiff(hotClock, mNextNeedTime));
// if we are a trickle channel, then don't try sending more until trickleRate has expired. We are only allowed
// to send up to trickleBytes at a time every trickleRate milliseconds; however, if we don't send the full trickleBytes
// in one GiveTime call, then it won't get to try sending more bytes until this timer has expired, even if we had not used
// up the entire trickleBytes allotment the last time we were in here...this should not cause any significant problems
if (mConfig.trickleRate > 0)
{
int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock);
if (nextAllowedSendTime > 0)
return(nextAllowedSendTime);
}
// lot a tweaking goes into calculating the optimal resend time. Set it too large and you can stall the pipe
// at the beginning of the connection fairly easily
int optimalResendDelay = (mAveragePingTime * mConfig.resendDelayPercent / 100) + mConfig.resendDelayAdjust; // percentage of average ping plus a fixed amount
optimalResendDelay = udpMin(mConfig.resendDelayCap, optimalResendDelay); // never let the resend delay get over max
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// see if any of the physical packets can actually be sent (either resends, or initial sends, whatever
// if not, calculate when exactly somebody is expected to need sending
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mMaxxedOutCurrentWindow = false;
int outstandingNextSendTime = 10 * 60000;
// if we have something to do
if (mReliableOutgoingPendingId < mReliableOutgoingId || mLogicalPacketList.Count() != 0 || mCoalescePacket != NULL)
{
// first, let's calculate how many bytes we figure is outstanding based on who is still waiting for an ack-packet
UdpClockStamp oldestResendTime = udpMax(hotClock - optimalResendDelay, mLastTimeStampAcknowledged); // anything older than this, we need to resend
bool readyQueueOverflow;
do
{
readyQueueOverflow = false;
int useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
int outstandingBytes = 0;
enum { cReadyQueueSize = 1000 };
PhysicalPacket *readyQueue[cReadyQueueSize];
PhysicalPacket **readyEnd = readyQueue + (sizeof(readyQueue) / sizeof(PhysicalPacket *));
PhysicalPacket **readyPtr = readyQueue;
int windowSpaceLeft = useMaxOutstandingBytes;
for (udp_int64 i = mReliableOutgoingPendingId; i <= mReliableOutgoingId; i++)
{
if (i == mReliableOutgoingId)
{
// this packet is not really here yet, we need to pull it down if possible
// if not possible, we need to break out of the loop as we are done
if (!PullDown(windowSpaceLeft))
break;
}
// if this packet has not been acked and it is NOT ready to be sent (was recently sent) then we consider it outstanding
// note: packets needing re-sending probably got lost and are therefore not outstanding
PhysicalPacket *entry = &mPhysicalPackets[i % mConfig.maxOutstandingPackets];
if (entry->mDataPtr != NULL) // acked packets set the dataPtr to NULL
{
// if this packet is ready to be sent (ie: needs time now, or some later packet has already been ack'ed)
windowSpaceLeft -= entry->mDataLen; // window-space is effectively taken whether we have sent it yet or not
if (entry->mLastTimeStamp < oldestResendTime)
{
if (readyPtr < readyEnd) // if we have queue space
{
*readyPtr++ = entry;
}
else
{
// we ran out of space in the ready-queue, so we are going to have to do this whole process
// over again. Normally we would just want to make sure the readyQueue was really big to save
// us doing all this stuff twice; however, that uses up a ton of stack space we may not have
// and really only happens in situations where there is a ton of stuff waiting to be sent
// (ie, super high bandwidth situations really). So I don't mind taking a performance hit
// for those situations to keep the stack size small for the vast majority of normal situations.
// note: this was initially added in support of the tiny stack the PS3 had.
readyQueueOverflow = true;
}
}
else
{
outstandingBytes += entry->mDataLen;
outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay - UdpMisc::ClockDiff(entry->mLastTimeStamp, hotClock));
}
// if we have reached a point in the queue where there are no sent packets
// and our outstanding bytes plus how much we intend to send is greater than the window we have
// then we can quit step-2, since there is nothing else to be gained by continuing.
if (entry->mFirstTimeStamp == 0 && (windowSpaceLeft <= 0))
break;
}
}
// second, send ready entries until the max outstanding is reached
int toleranceLossCount = 0;
bool allowWindowReset = (UdpMisc::ClockDiff(mWindowResetTime, hotClock) > mAveragePingTime); // only allow it to reset the window again if it has been longer than the average ping time
int trickleSent = 0;
PhysicalPacket **readyWalk = readyQueue;
PhysicalPacket *pendingReliableBasePtr = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets];
while (readyWalk < readyPtr && outstandingBytes < useMaxOutstandingBytes)
{
// prepare packet and send it
PhysicalPacket *entry = *readyWalk++;
// prepare reliable header and send it with data
const udp_uchar *parentBase = (const udp_uchar *)entry->mParent->GetDataPtr();
bool fragment = false;
if (entry->mDataPtr != parentBase || entry->mDataLen != entry->mParent->GetDataLen())
fragment = true;
// we can calculate what our reliableId should be based on our position in the array
// need to handle the case where we wrap around the end of the array
udp_int64 reliableId;
if (entry >= pendingReliableBasePtr)
{
reliableId = mReliableOutgoingPendingId + (entry - pendingReliableBasePtr);
}
else
{
reliableId = mReliableOutgoingPendingId + (&mPhysicalPackets[mConfig.maxOutstandingPackets] - pendingReliableBasePtr) + (entry - &mPhysicalPackets[0]);
}
// prep the actual packet and send it
bufPtr = buf;
*bufPtr++ = 0;
*bufPtr++ = (udp_uchar)(((fragment) ? UdpConnection::cUdpPacketFragment1 : UdpConnection::cUdpPacketReliable1) + mChannelNumber); // mark us as a fragment if we are one
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)(reliableId & 0xffff));
if (fragment && entry->mDataPtr == parentBase)
bufPtr += UdpMisc::PutValue32(bufPtr, entry->mParent->GetDataLen()); // first fragment has a total-length byte after the reliable header
mUdpConnection->BufferedSend(buf, (int)(bufPtr - buf), entry->mDataPtr, entry->mDataLen, false);
// update state information
if (entry->mFirstTimeStamp == 0)
{
entry->mFirstTimeStamp = hotClock;
}
else
{
// trying to send the packet again, let's see how long we have been trying to send this packet. If we
// have an unacknowledged timeout set and it is older than that, then terminate the connection.
// note: we only check for the oldest unacknowledged age against the timeout at the point in time
// that we are considering sending the packet again. This can technically cause it to wait slightly
// longer than the specified timeout setting before disconnecting the connection, but should be
// close enough for all practical purposes and allows for more efficient processing of this setting internally.
if (mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout > 0)
{
int age = UdpMisc::ClockDiff(entry->mFirstTimeStamp, hotClock);
if (age > mUdpConnection->mUdpManager->mParams.oldestUnacknowledgedTimeout)
{
mUdpConnection->InternalDisconnect(0, UdpConnection::cDisconnectReasonUnacknowledgedTimeout);
return(0);
}
}
// were we resent because of a later ack came in? or because we timed out?
if (entry->mLastTimeStamp < mLastTimeStampAcknowledged)
{
// we are resending this packet due to an accelleration (receiving a later packet ack)
// so recalc slow start threshhold and congestion window size as per Reno fast-recovery algorithm
if (allowWindowReset && toleranceLossCount > mConfig.toleranceLossCount)
{
allowWindowReset = false;
mWindowResetTime = hotClock;
mCongestionWindowSize = mCongestionWindowSize * 3 / 4;
mCongestionWindowSize = udpMax(mCongestionWindowMinimum, mCongestionWindowSize); // never let congestion window get smaller than a single packet
mCongestionSlowStartThreshhold = mCongestionWindowSize;
useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
}
// when resends are caused by a selective ack, that means later data is making it, so we may not be overloading
// the connection after all. Reno fast recovery calls for less shrinking of the window in this circumstance
// already. This tolerance code allows us to do even better, by allowing us to experience some small amount
// of packetloss without immediately considering such loss to be indicative that the connection is being overloaded.
// It does this by only allowing the window to get reset in cases where teh selective-ack forces a certain number
// of packets to be accellerated. If the application sets the tolerance at something like 10, then isolated packet
// losses will have no effect on the flow control window, yet a burst of loss of 10 would more strongly indicate
// a overloaded condition. Typically an application will only set this tolerance if they need extremely high
// bandwidth (ie, can't afford the window getting reset), and are known to be on a connection that may experience
// random packetloss. This is typical of a LFN type network.
toleranceLossCount++;
mStatResentPacketsAccelerated++;
mUdpConnection->mConnectionStats.resentPacketsAccelerated++;
mUdpConnection->mUdpManager->IncrementResentPacketsAccelerated();
}
else
{
// we are resending this packet due to a timeout, so we are seriously overloading things probably
// so recalc slow start threshhold and congestion window size as per Reno algorithm
// no tolerance on timed-out window resets, since by definition you have not received later acks
// or this would be an accellerated timeout, which means that either your volume is really low
// anyways (so resetting the window is fine), or you seriously overloaded the other side as nothing
// later made it either.
if (allowWindowReset)
{
allowWindowReset = false;
mWindowResetTime = hotClock;
mCongestionSlowStartThreshhold = udpMax(mMaxDataBytes * 2, mCongestionWindowSize / 2);
mCongestionWindowSize = mCongestionWindowStart;
useMaxOutstandingBytes = udpMin(mConfig.maxOutstandingBytes, mCongestionWindowSize);
// because a resend has occurred due to a timeout, slow down the resend times slightly
// when things start flowing again, it will fix itself up quickly anyways
mAveragePingTime += 100;
// When a connection goes temporarily dead, everything that is in the current
// window will end up getting timedout. If the window were large, this could result in the
// mAveragePingTime growing quite large and creating very long stalls in the pipe once it does
// start moving again. To prevent this, we cap mAveragePingTime when these events occur to prevent
// long stalls when the pipe finally reopens.
mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime);
}
mStatResentPacketsTimedOut++;
mUdpConnection->mConnectionStats.resentPacketsTimedOut++;
mUdpConnection->mUdpManager->IncrementResentPacketsTimedOut();
}
}
entry->mLastTimeStamp = hotClock;
outstandingNextSendTime = udpMin(outstandingNextSendTime, optimalResendDelay); // this packet is now outstanding, so factor it into the outstandingNextSendTime calculation
outstandingBytes += entry->mDataLen;
mTrickleLastSend = hotClock;
trickleSent += entry->mDataLen;
if (mConfig.trickleSize != 0 && trickleSent >= mConfig.trickleSize)
break;
}
if (outstandingBytes >= useMaxOutstandingBytes)
{
mMaxxedOutCurrentWindow = true;
}
// if we filled up the ready queue (ie, if we may still have data to send)
// and we still have space left in the flow window, then repeat the process.
// this should only happen when the flow-control window is enormous.
} while (readyQueueOverflow && !mMaxxedOutCurrentWindow);
}
else
{
// we have nothing in the pipe at all, reset the congestion window (this means everything has been acked, so the pipe is totally empty
// we need to reset the window back to prevent a sudden flood next time a large chunk of data is sent.
// we also need to avoid having the slowly sent reliable packets constantly increase the window size (since none will ever get lost)
// such that when it does come time to send a big chunk of data, it thinks the window-size is enormous.
// resetting the window back to small will only have an effect if a large chunk of data is then sent, at which time it will quickly
// ramp up with the slow-start method.
mCongestionWindowSize = mCongestionWindowStart;
}
// printf("%d,%d\n", mCongestionWindowSize, mCongestionSlowStartThreshhold); // useful debug output for graphing congestion window
int nextAllowedSendTime = mConfig.trickleRate - UdpMisc::ClockDiff(mTrickleLastSend, hotClock);
nextAllowedSendTime = udpMax(0, udpMax(nextAllowedSendTime, outstandingNextSendTime));
mNextNeedTime = hotClock + nextAllowedSendTime;
return(nextAllowedSendTime);
}
void UdpReliableChannel::GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const
{
int coalesceBytes = 0;
if (mCoalescePacket != NULL)
coalesceBytes = (int)(mCoalesceEndPtr - mCoalesceStartPtr);
channelStatus->totalPendingBytes = mLogicalBytesQueued + mReliableOutgoingBytes + coalesceBytes;
channelStatus->queuedPackets = mLogicalPacketList.Count();
channelStatus->queuedBytes = mLogicalBytesQueued;
channelStatus->incomingLargeTotal = mBigDataTargetLen;
channelStatus->incomingLargeSoFar = mBigDataLen;
channelStatus->duplicatePacketsReceived = mStatDuplicatePacketsReceived;
channelStatus->resentPacketsAccelerated = mStatResentPacketsAccelerated;
channelStatus->resentPacketsTimedOut = mStatResentPacketsTimedOut;
channelStatus->congestionSlowStartThreshhold = mCongestionSlowStartThreshhold;
channelStatus->congestionWindowSize = mCongestionWindowSize;
channelStatus->ackAveragePing = mAveragePingTime;
channelStatus->oldestUnacknowledgedAge = 0;
if (mReliableOutgoingPendingId < mReliableOutgoingId)
{
// oldest pending packet will be definition be the oldestUnacknowledged, since it is impossible
// for any packet after it to have possibly been sent before it was sent for its first time
// since queue is effectively in first-send order. It is also impossible for this packet to have
// been acknowledged, since the pending id advances the moment the oldest is acknowledged, meaning
// that the pendingId is always pointing to something that has either been sent (or not sent at all)
PhysicalPacket *entry = &mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets];
if (entry->mFirstTimeStamp != 0) // if has been sent (we know it hasn't been acknowledged or we couldn't possibly be pointing at it as pending)
{
if (mUdpConnection->GetUdpManager() != NULL)
{
channelStatus->oldestUnacknowledgedAge = mUdpConnection->GetUdpManager()->CachedClockElapsed(entry->mFirstTimeStamp);
}
}
}
}
void UdpReliableChannel::ReliablePacket(const udp_uchar *data, int dataLen)
{
udp_uchar buf[256];
udp_uchar *bufPtr;
if (dataLen <= UdpConnection::cUdpPacketReliableSize)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonReliablePacketTooShort);
return;
}
int packetType = data[1];
udp_ushort reliableStamp = UdpMisc::GetValue16(data + 2);
udp_int64 reliableId = GetReliableIncomingId(reliableStamp);
if (reliableId >= mReliableIncomingId + mConfig.maxInstandingPackets)
return; // if we do not have buffer space to hold onto this packet, then we simply must pretend like it was lost
if (reliableId >= mReliableIncomingId)
{
ReliablePacketMode mode = (ReliablePacketMode)((packetType - UdpConnection::cUdpPacketReliable1) / cReliableChannelCount);
// is this the packet we are waiting for
if (mReliableIncomingId == reliableId)
{
// if so, process it immediately
ProcessPacket(mode, data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize);
mReliableIncomingId++;
// process other packets that have arrived
while (mReliableIncoming[mReliableIncomingId % mConfig.maxInstandingPackets].mPacket != NULL)
{
int spot = (int)(mReliableIncomingId % mConfig.maxInstandingPackets);
if (mReliableIncoming[spot].mMode != cReliablePacketModeDelivered)
{
ProcessPacket(mReliableIncoming[spot].mMode, (udp_uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen());
}
mReliableIncoming[spot].mPacket->Release();
mReliableIncoming[spot].mPacket = NULL;
mReliableIncomingId++;
}
}
else
{
// not the one we need next, but it is later than the one we need , so store it in our buffer until it's turn comes up
int spot = (int)(reliableId % mConfig.maxInstandingPackets);
if (mReliableIncoming[spot].mPacket == NULL) // only make the copy of it if we don't already have it in our buffer (in cases where it was sent twice, there would be no harm in the copy again since it must be the same packet, it's just inefficient)
{
mReliableIncoming[spot].mMode = mode;
mReliableIncoming[spot].mPacket = mUdpConnection->mUdpManager->CreatePacket(data + UdpConnection::cUdpPacketReliableSize, dataLen - UdpConnection::cUdpPacketReliableSize);
// on out of order deliver, we need to keep a copy of it as if we were doing ordered-delivery in order to prevent duplicates
// we will mark the packet in the queue as already delivered to prevent it from getting delivered a second time when the stalled packet
// arrives and unwinds the queue
if (mode == cReliablePacketModeReliable && mConfig.outOfOrder)
{
ProcessPacket(cReliablePacketModeReliable, (udp_uchar *)mReliableIncoming[spot].mPacket->GetDataPtr(), mReliableIncoming[spot].mPacket->GetDataLen());
mReliableIncoming[spot].mMode = cReliablePacketModeDelivered;
}
}
else
{
mStatDuplicatePacketsReceived++;
mUdpConnection->mConnectionStats.duplicatePacketsReceived++;
mUdpConnection->mUdpManager->IncrementDuplicatePacketsReceived();
}
}
}
else
{
mStatDuplicatePacketsReceived++;
mUdpConnection->mConnectionStats.duplicatePacketsReceived++;
mUdpConnection->mUdpManager->IncrementDuplicatePacketsReceived();
}
bool ackAll = false;
bufPtr = buf;
*bufPtr++ = 0;
if (mReliableIncomingId > reliableId)
{
// ack everything up to the current head of our chain (minus one since the stamp represents the next one we want to get)
*bufPtr++ = (udp_uchar)(UdpConnection::cUdpPacketAckAll1 + mChannelNumber);
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)((mReliableIncomingId - 1) & 0xffff));
ackAll = true;
}
else
{
// a simple ack for us only
*bufPtr++ = (udp_uchar)(UdpConnection::cUdpPacketAck1 + mChannelNumber);
bufPtr += UdpMisc::PutValue16(bufPtr, (udp_ushort)(reliableId & 0xffff));
}
if (mBufferedAckPtr != NULL && mConfig.ackDeduping && ackAll)
{
memcpy(mBufferedAckPtr, buf, bufPtr - buf);
}
else
{
udp_uchar *ptr = mUdpConnection->BufferedSend(buf, (int)(bufPtr - buf), NULL, 0, true); // safe to append on our data, it is stack data
if (mBufferedAckPtr == NULL)
{
// the buffered-ack ptr should always point to the earliest ack in the buffer, such that
// a replacement ack-all will be processed by the receiver before any selective acks that may
// have been buffered up. Thus, only repoint the buffered ack ptr if it was previously unset.
mBufferedAckPtr = ptr;
}
}
}
void UdpReliableChannel::ProcessPacket(ReliablePacketMode mode, const udp_uchar *data, int dataLen)
{
assert(dataLen > 0);
// if we have a big packet under construction already, or we are a fragment and thus need to be constructing one, then append this on the end (will create new if it is the first fragment)
if (mode == cReliablePacketModeReliable)
{
// we are not a fragment, nor was there a fragment in progress, so we are a simple reliable packet, just send it to the app
if (mBigDataPtr != NULL)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentExpected);
return;
}
mUdpConnection->ProcessCookedPacket(data, dataLen);
}
else if (mode == cReliablePacketModeFragment)
{
// append onto end of big packet (or create new big packet if not existing already)
if (mBigDataPtr == NULL)
{
if (dataLen < 4)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentBad);
return;
}
mBigDataTargetLen = UdpMisc::GetValue32(data); // first fragment has a total-length int header on it.
if (mBigDataTargetLen <= 0) // negative or zero sized chunk to follow means packet corruption or tampering for sure
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentBad);
return;
}
if (mBigDataTargetLen > mUdpConnection->mUdpManager->mParams.incomingLogicalPacketMax)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonFragmentOversized);
return;
}
mBigDataPtr = new udp_uchar[mBigDataTargetLen];
mBigDataLen = 0;
data += 4;
dataLen -= 4;
}
int safetyMax = udpMin(mBigDataTargetLen - mBigDataLen, dataLen); // can't happen in theory since they should add up exact, but protect against it if it does
assert(safetyMax == dataLen);
memcpy(mBigDataPtr + mBigDataLen, data, safetyMax);
mBigDataLen += safetyMax;
if (mBigDataTargetLen == mBigDataLen)
{
// send big-packet off to application
mUdpConnection->ProcessCookedPacket(mBigDataPtr, mBigDataLen);
// delete big packet, and reset
delete[] mBigDataPtr;
mBigDataLen = 0;
mBigDataTargetLen = 0;
mBigDataPtr = NULL;
}
}
}
void UdpReliableChannel::AckAllPacket(const udp_uchar *data, int dataLen)
{
if (dataLen < 4)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonAckBad);
return;
}
udp_int64 reliableId = GetReliableOutgoingId((udp_ushort)UdpMisc::GetValue16(data + 2));
if (mReliableOutgoingPendingId > reliableId)
{
// if we ackall'ed a packet and everything before the ackall address had already been acked, then we know
// for certainty that we sent a packet over again that did not need to be sent over again (ie. wasn't lost, just slow)
// so adjust the mAveragePingTime upward to slow down future resends
mAveragePingTime += 400;
mAveragePingTime = udpMin(mConfig.resendDelayCap, mAveragePingTime);
}
for (udp_int64 i = mReliableOutgoingPendingId; i <= reliableId; i++)
{
Ack(i);
}
}
void UdpReliableChannel::Ack(udp_int64 reliableId)
{
// if packet being acknowledged is possibly in our resend queue, then check for it
if (reliableId >= mReliableOutgoingPendingId && reliableId < mReliableOutgoingId)
{
int pos = (int)(reliableId % mConfig.maxOutstandingPackets);
PhysicalPacket *entry = &mPhysicalPackets[pos];
if (entry->mDataPtr != NULL) // if this packet has not been acknowledged yet (sometimes we get back two acks for the same packet)
{
mNextNeedTime = 0; // something got acked, so we actually need to take the time next time it is offered
// if the last time we gave this reliable channel processing time, it filled up the entire sliding window
// then go ahead and increase the window-size when incoming acks come in. However, if the window wasn't full
// then don't increase the window size. The problem is, a game application is likely to send reliable data
// at a relatively slow rate (2k/second for example), never filling the window. The net result would be that
// every acknowledged packet would increase the window size, giving the reliable channel the impression that
// it's window can be very very large, when in fact, it is only not losing packets because the application is pacing
// itself. The window could grow enormous, even 200k for a modem. Then, if the application were to dump a load
// of data onto us all at once, it would flush it all out at once thinking it had a big window. By only increasing
// the window size when we have high enough volume to fill the window, we ensure this does not happen. TCP does a similar
// thing, but what they do is reset the window if there has been a long stall. We do that too, but because we are a game
// application that is likely to pace the data at the application level, we have a unique circumstances that need addressing.
if (mMaxxedOutCurrentWindow)
{
if (mCongestionWindowSize < mCongestionSlowStartThreshhold)
{
mCongestionWindowSize += mMaxDataBytes; // slow-start mode
}
else
{
int increase = (mMaxDataBytes * mMaxDataBytes / mCongestionWindowSize);
mCongestionWindowSize += udpMax(1, increase); // congestion mode
}
}
if (entry->mLastTimeStamp == entry->mFirstTimeStamp)
{
// if the packet that is being acknowledged was only sent once, then we can safely use
// the round-trip time as an accurate measure of ping time. By knowing ping time, we can
// better (more agressively) schedule resends of lost packets. We will use a moving average
// that weights the current packet as 1/4 the average.
int thisPingTime = mUdpConnection->GetUdpManager()->CachedClockElapsed(entry->mFirstTimeStamp);
mAveragePingTime = (mAveragePingTime * 3 + thisPingTime) / 4;
}
// what this is doing is if we receive an ACK for a packet that was sent at TIME_X
// we can assume that all packets sent before TIME_X that are not yet acknowledge were lost
// and we can resend them immediately
// since we do not know whether this ack is for last packet we sent, we have to assume it is for the first time this packet was sent (if sent multiple times)
// otherwise, we could resend it, then receive the ack from the first packet, and think our last-ack time is the time of the second outgoing packet
// which would cause about every packet in the queue to resend, even if they had just been sent
// in situations where the first packet truely was lost and this is an ACK of the second packet, then the only
// harm done is that the we may not resend some of the earlier sent packets quite as quickly. This will only
// happen in situations where a packet that was truely lost gets acked on it's second attempt...we just
// won't be using that ack for the purposes of accelerating other resends...since odds are a non-lost packet
// will accelerate those other resends shortly anyhow, there really is no loss
// (note: we used to only set this value forward for packets that were never lost (one time sends); however, if this stamp
// ever got set way high for some reason (in theory it can't happen), then we would get into a situation where it would
// rapidly resend and possibly never get reset, causing infinite rapid resends, so we now set it every time to the first-stamp)
// which will be safe, even if the packet were resent.)
mLastTimeStampAcknowledged = entry->mFirstTimeStamp;
// this packet we have queued has been acknowledged, so delete it from queue
mReliableOutgoingBytes -= entry->mDataLen;
entry->mDataLen = 0;
entry->mDataPtr = NULL;
entry->mParent->Release();
entry->mParent = NULL;
// advance the pending ptr until it reaches outgoingId or an entry that has yet to acknowledged
while (mReliableOutgoingPendingId < mReliableOutgoingId)
{
if (mPhysicalPackets[mReliableOutgoingPendingId % mConfig.maxOutstandingPackets].mDataPtr != NULL)
break;
mReliableOutgoingPendingId++;
}
}
else
{
// we got an ack for a packet that has already been acked. This could be due to an ack-all packet that covered us so statistically
// we can't do much with this information.
}
}
// we don't need to try rescheduling ourself here, since our connection object reschedules to go immediately whenever any type
// of packet arrives (including ack packets)
}
UdpReliableChannel::IncomingQueueEntry::IncomingQueueEntry()
{
mPacket = NULL;
mMode = UdpReliableChannel::cReliablePacketModeReliable;
}
UdpReliableChannel::IncomingQueueEntry::~IncomingQueueEntry()
{
if (mPacket != NULL)
mPacket->Release();
}
UdpReliableChannel::PhysicalPacket::PhysicalPacket()
{
mParent = NULL;
}
UdpReliableChannel::PhysicalPacket::~PhysicalPacket()
{
if (mParent != NULL)
mParent->Release();
}
} // namespace
@@ -0,0 +1,177 @@
#ifndef UDPLIBRARY_UDPRELIABLECHANNEL_H
#define UDPLIBRARY_UDPRELIABLECHANNEL_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
namespace UdpLibrary
{
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The purpose of this class is to manage the reliable transmission of packets on top of the inherently
// unreliable UDP layer. This is an internal object and should not be manually created or talked to by the user.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
class UdpReliableChannel
{
protected:
friend class UdpConnection;
UdpReliableChannel(int channelNumber, UdpConnection *connection, UdpReliableConfig *config);
~UdpReliableChannel();
void GetChannelStatus(UdpConnection::ChannelStatus *channelStatus) const;
int GetAveragePing() const;
int TotalPendingBytes() const; // returns total bytes outstanding
void ReliablePacket(const udp_uchar *data, int dataLen);
void Send(const udp_uchar *data, int dataLen, const udp_uchar *data2, int dataLen2);
void AckPacket(const udp_uchar *data, int dataLen);
void AckAllPacket(const udp_uchar *data, int dataLen);
void ClearBufferedAck();
int GiveTime();
protected:
enum ReliablePacketMode { cReliablePacketModeReliable, cReliablePacketModeFragment, cReliablePacketModeDelivered };
class PhysicalPacket
{
public:
PhysicalPacket();
~PhysicalPacket();
public:
UdpClockStamp mFirstTimeStamp;
UdpClockStamp mLastTimeStamp;
const LogicalPacket *mParent; // physical packets hold an addref on the logical packet. Once all of the logical packet data has been divied out to physical packets, the logical queue releases it
const udp_uchar *mDataPtr; // within parent's data (it's possible it is not pointing to the beginning in the case of large packets)
int mDataLen;
};
class IncomingQueueEntry
{
public:
IncomingQueueEntry();
~IncomingQueueEntry();
public:
LogicalPacket *mPacket;
ReliablePacketMode mMode;
};
friend class IncomingQueueEntry;
udp_int64 GetReliableOutgoingId(int reliableStamp) const;
udp_int64 GetReliableIncomingId(int reliableStamp) const;
void Ack(udp_int64 reliableId);
void ProcessPacket(ReliablePacketMode mode, const udp_uchar *data, int dataLen);
bool PullDown(int windowSpaceLeft);
void FlushCoalesce();
void SendCoalesce(const udp_uchar *data, int dataLen, const udp_uchar *data2 = NULL, int dataLen2 = 0);
void QueueLogicalPacket(LogicalPacket *packet);
UdpReliableConfig mConfig;
UdpConnection *mUdpConnection;
UdpClockStamp mLastTimeStampAcknowledged;
UdpClockStamp mTrickleLastSend;
UdpClockStamp mNextNeedTime;
UdpClockStamp mWindowResetTime;
int mChannelNumber;
udp_int64 mReliableOutgoingId;
udp_int64 mReliableOutgoingPendingId;
int mReliableOutgoingBytes;
int mLogicalBytesQueued;
udp_uchar *mBigDataPtr;
int mBigDataLen;
int mBigDataTargetLen;
int mAveragePingTime;
int mMaxDataBytes;
int mFragmentNextPos;
PhysicalPacket *mPhysicalPackets;
UdpLinkedList<LogicalPacket> mLogicalPacketList;
int mCongestionWindowStart;
int mCongestionWindowSize;
int mCongestionSlowStartThreshhold;
int mCongestionWindowMinimum;
bool mMaxxedOutCurrentWindow;
udp_int64 mReliableIncomingId;
IncomingQueueEntry *mReliableIncoming;
LogicalPacket *mCoalescePacket;
udp_uchar *mCoalesceStartPtr;
udp_uchar *mCoalesceEndPtr;
int mCoalesceCount;
int mMaxCoalesceAttemptBytes;
udp_uchar *mBufferedAckPtr;
int mStatDuplicatePacketsReceived;
int mStatResentPacketsAccelerated;
int mStatResentPacketsTimedOut;
};
/////////////////////////////////////////////////////////////////////////
// inline implementations
/////////////////////////////////////////////////////////////////////////
// UdpReliableChannel
inline void UdpReliableChannel::AckPacket(const udp_uchar *data, int dataLen)
{
if (dataLen < 4)
{
mUdpConnection->CallbackCorruptPacket(data, dataLen, cUdpCorruptionReasonAckBad);
return;
}
Ack(GetReliableOutgoingId((udp_ushort)UdpMisc::GetValue16(data + 2)));
}
inline int UdpReliableChannel::GetAveragePing() const
{
return(mAveragePingTime);
}
inline int UdpReliableChannel::TotalPendingBytes() const
{
return(mLogicalBytesQueued + mReliableOutgoingBytes);
}
inline void UdpReliableChannel::ClearBufferedAck()
{
mBufferedAckPtr = NULL;
}
inline udp_int64 UdpReliableChannel::GetReliableOutgoingId(int reliableStamp) const
{
// since we can never have anywhere close to 65000 packets outstanding, we only need to
// to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck
// packets, because we can reconstruct the full id from that, we just need to take
// into account the wrap around issue. We calculate it based of the high-word of the
// next packet we are going to send. If it ends up being larger then we know
// we wrapped and can fix it up by simply subtracting 1 from the high-order word.
udp_int64 reliableId = reliableStamp | (mReliableOutgoingId & (~(udp_int64)0xffff));
if (reliableId > mReliableOutgoingId)
reliableId -= 0x10000;
return(reliableId);
}
inline udp_int64 UdpReliableChannel::GetReliableIncomingId(int reliableStamp) const
{
// since we can never have anywhere close to 65000 packets outstanding, we only need to
// to send the low order word of the reliableId in the UdpPacketReliable and UdpPacketAck
// packets, because we can reconstruct the full id from that, we just need to take
// into account the wrap around issue. We basically prepend the last-known
// high-order word. If we end up significantly below the head of our chain, then we
// know we need to pick the entry 0x10000 higher. If we fall significantly above
// our previous high-end, then we know we need to go the other way.
udp_int64 reliableId = reliableStamp | (mReliableIncomingId & (~(udp_int64)0xffff));
if (reliableId < mReliableIncomingId - UdpManager::cHardMaxOutstandingPackets)
reliableId += 0x10000;
if (reliableId > mReliableIncomingId + UdpManager::cHardMaxOutstandingPackets)
reliableId -= 0x10000;
return(reliableId);
}
} // namespace
#endif
@@ -0,0 +1,119 @@
#ifndef UDPLIBRARY_UDPTYPES_H
#define UDPLIBRARY_UDPTYPES_H
// Copyright 2004 Sony Online Entertainment, all rights reserved.
// Author: Jeff Petersen
#include <assert.h>
namespace UdpLibrary
{
class UdpManager;
class UdpConnection;
class LogicalPacket;
class UdpPlatformAddress;
#if defined(WIN32)
typedef __int64 udp_int64;
#pragma warning(disable:4121) // an error in microsofts compiler makes it think that pointers-to-members require 12 byte alignment, and since we are only doing 8 byte alignment by default, we get this warning. Turns out that alignment is actually a non-issue for pointers-to-members since they are accessed only 4 bytes at a time. (found this info on the internet)
#else
typedef long long udp_int64;
#endif
typedef unsigned char udp_uchar;
typedef unsigned short udp_ushort;
typedef unsigned int udp_uint;
typedef unsigned long udp_ulong;
typedef udp_int64 UdpClockStamp;
typedef UdpPlatformAddress UdpIpAddress; // deprecated, use UdpPlatformAddress directly instead
enum UdpCorruptionReason { cUdpCorruptionReasonNone
, cUdpCorruptionReasonMultiPacket
, cUdpCorruptionReasonReliablePacketTooShort
, cUdpCorruptionReasonInternalPacketTooShort
, cUdpCorruptionReasonDecryptFailed
, cUdpCorruptionReasonZeroLengthPacket
, cUdpCorruptionReasonPacketShorterThanCrcBytes
, cUdpCorruptionReasonMisformattedGroup
, cUdpCorruptionReasonFragmentOversized
, cUdpCorruptionReasonFragmentBad
, cUdpCorruptionReasonFragmentExpected
, cUdpCorruptionReasonAckBad
};
enum UdpChannel { cUdpChannelUnreliable // unreliable/unordered/buffered
, cUdpChannelUnreliableUnbuffered // unreliable/unordered/unbuffered
, cUdpChannelOrdered // unreliable/ordered/buffered
, cUdpChannelOrderedUnbuffered // unreliable/ordered/unbuffered
, cUdpChannelReliable1 // reliable (as per channel config)
, cUdpChannelReliable2 // reliable (as per channel config)
, cUdpChannelReliable3 // reliable (as per channel config)
, cUdpChannelReliable4 // reliable (as per channel config)
, cUdpChannelCount // count of number of channels
};
class UdpRefCount
{
public:
UdpRefCount();
virtual void AddRef() const;
virtual void Release() const;
virtual void NoRef() const;
virtual int GetRefCount() const;
protected:
virtual ~UdpRefCount();
mutable int mRefCount;
mutable bool mNoRef;
};
/////////////////////////////////////////////////////////////////////////////////
// UdpRefCount implementation (entirely inline)
/////////////////////////////////////////////////////////////////////////////////
inline UdpRefCount::UdpRefCount()
{
mRefCount = 1;
mNoRef = false;
}
inline UdpRefCount::~UdpRefCount()
{
assert(mRefCount == 0 || mNoRef);
}
inline void UdpRefCount::AddRef() const
{
assert(!mNoRef);
assert(mRefCount > 0);
++mRefCount;
}
inline void UdpRefCount::Release() const
{
assert(!mNoRef);
assert(mRefCount > 0);
if (--mRefCount == 0)
{
delete this;
}
}
inline void UdpRefCount::NoRef() const
{
assert(mRefCount == 1);
mNoRef = true;
}
inline int UdpRefCount::GetRefCount() const
{
return(mRefCount);
}
} // namespace
#endif