#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 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 mAvailableLink; // for available linked list in manager (used by reliable channel object as well while it is borrowed) UdpLinkedListMember mCreatedLink; // for created linked list in manager }; template 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 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 mAvailableLink; // for available linked list in manager UdpLinkedListMember mCreatedLink; // for created linked list in manager }; ///////////////////////////////////////////////////////////////////////// // FixedLogicalPacket implementation ///////////////////////////////////////////////////////////////////////// template FixedLogicalPacket::FixedLogicalPacket(const void *data, int dataLen) { mDataLen = dataLen; if (data != NULL) memcpy(mData, data, mDataLen); } template void *FixedLogicalPacket::GetDataPtr() { return(mData); } template const void *FixedLogicalPacket::GetDataPtr() const { return(mData); } template int FixedLogicalPacket::GetDataLen() const { return(mDataLen); } template void FixedLogicalPacket::SetDataLen(int len) { mDataLen = len; } ///////////////////////////////////////////////////////////////////////// // StructLogicalPacket implementation ///////////////////////////////////////////////////////////////////////// template StructLogicalPacket::StructLogicalPacket(T *initData) { if (initData != NULL) mStruct = *initData; } template void *StructLogicalPacket::GetDataPtr() { return(&mStruct); } template const void *StructLogicalPacket::GetDataPtr() const { return(&mStruct); } template int StructLogicalPacket::GetDataLen() const { return(sizeof(mStruct)); } template void StructLogicalPacket::SetDataLen(int len) { } } // namespace #endif