This commit is contained in:
2025-11-24 14:19:51 +05:30
commit f5c1412b28
6734 changed files with 1527575 additions and 0 deletions

View File

@ -0,0 +1,191 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: React to timeouts from DNS server by switching to a backup
// TODO: Use TTL from DNS record instead of fixed constant
// TODO: If the DNS resolution load is high, it would make sense to put
// multiple requests in the same DNS packet
// TODO: Retransmissions could also be grouped into the same DNS packets
// TODO: The locks held in DNSClient are fairly coarse and could be broken up
#ifndef CAT_DNS_CLIENT_HPP
#define CAT_DNS_CLIENT_HPP
#include <cat/net/ThreadPoolSockets.hpp>
#include <cat/threads/Thread.hpp>
#include <cat/threads/WaitableFlag.hpp>
#include <cat/crypt/rand/Fortuna.hpp>
#include <cat/port/FastDelegate.h>
namespace cat {
static const int HOSTNAME_MAXLEN = 63; // Max characters in a hostname request
static const int DNSREQ_TIMEOUT = 3000; // DNS request timeout interval
static const int DNSREQ_REPOST_TIME = 300; // Number of milliseconds between retries
static const int DNSREQ_MAX_SIMUL = 2048; // Maximum number of simultaneous DNS requests
static const int DNSCACHE_MAX_REQS = 8; // Maximum number of requests to cache
static const int DNSCACHE_MAX_RESP = 8; // Maximum number of responses to cache
static const int DNSCACHE_TIMEOUT = 60000; // Time until a cached response is dropped
// Prototype: bool MyResultCallback(const char *, const NetAddr*, int);
typedef fastdelegate::FastDelegate3<const char *, const NetAddr*, int, bool> DNSResultCallback;
//// DNSRequest
struct DNSCallback
{
DNSCallback *next;
DNSResultCallback cb;
ThreadRefObject *ref;
};
struct DNSRequest
{
DNSRequest *last, *next;
u32 first_post_time; // Timestamp for first post, for timeout
u32 last_post_time; // Timestamp for last post, for retries and cache
// Our copy of the hostname string
char hostname[HOSTNAME_MAXLEN+1];
u16 id; // Random request ID
DNSCallback callback_head;
// For caching purposes
NetAddr responses[DNSCACHE_MAX_RESP];
int num_responses;
};
//// DNSClient
class DNSClient : Thread, public UDPEndpoint, public Singleton<DNSClient>
{
static const int TICK_RATE = 200; // milliseconds
static const int DNS_THREAD_KILL_TIMEOUT = 10000; // 10 seconds
CAT_SINGLETON(DNSClient)
: UDPEndpoint(REFOBJ_PRIO_0+5)
{
_server_addr.Invalidate();
_dns_unavailable = true;
_cache_head = _cache_tail = 0;
_cache_size = 0;
_request_head = _request_tail = 0;
_request_queue_size = 0;
}
public:
virtual ~DNSClient();
/*
In your startup code, call Initialize() and check the return value.
In your shutdown code, call Shutdown(). This will delete the DNSClient object.
*/
bool Initialize();
void Shutdown();
/*
If hostname is numeric or in the cache, the callback function will be invoked
immediately from the requesting thread, rather than from another thread.
First attempts numerical resolve of hostname, then queries the DNS server.
Hostname string length limited to HOSTNAME_MAXLEN characters.
Caches the most recent DNSCACHE_MAX_REQS requests.
Returns up to DNSCACHE_MAX_RESP addresses per resolve request.
Performs DNS lookup on a cached request after DNSCACHE_TIMEOUT.
Gives up on DNS lookup after DNSREQ_TIMEOUT.
If holdRef is valid, the reference will be held until the callback completes.
If no results were found, array_length == 0.
If the callback returns false, the result will not be entered into the cache.
The resolved addresses may need to be promoted to IPv6.
If Resolve() returns false, no callback will be generated.
*/
bool Resolve(const char *hostname, DNSResultCallback, ThreadRefObject *holdRef = 0);
private:
NetAddr _server_addr;
volatile bool _dns_unavailable;
FortunaOutput *_csprng;
private:
Mutex _request_lock;
DNSRequest *_request_head;
DNSRequest *_request_tail;
int _request_queue_size;
bool GetUnusedID(u16 &id); // not thread-safe, caller must lock
bool IsValidHostname(const char *hostname);
DNSRequest *PullRequest(u16 id); // not thread-safe, caller must lock
private:
Mutex _cache_lock;
DNSRequest *_cache_head;
DNSRequest *_cache_tail;
int _cache_size;
// These functions do not lock, caller must lock:
void CacheAdd(DNSRequest *req); // Assumes not already in cache
DNSRequest *CacheGet(const char *hostname); // Case-insensitive
void CacheKill(DNSRequest *req); // Assumes already in cache
private:
bool GetServerAddr();
bool BindToRandomPort(bool ignoreUnreachable);
bool PostDNSPacket(DNSRequest *req, u32 now);
bool PerformLookup(DNSRequest *req); // not thread-safe, caller must lock
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
virtual void OnClose();
virtual void OnUnreachable(const NetAddr &src);
protected:
void ProcessDNSResponse(DNSRequest *req, int qdcount, int ancount, u8 *data, u32 bytes);
void NotifyRequesters(DNSRequest *req);
private:
WaitableFlag _kill_flag;
bool ThreadFunction(void *param);
};
} // namespace cat
#endif // CAT_DNS_CLIENT_HPP

View File

@ -0,0 +1,188 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SOCKETS_HPP
#define CAT_SOCKETS_HPP
#include <cat/Platform.hpp>
#include <string>
#if defined(CAT_OS_WINDOWS)
# include <WS2tcpip.h>
#else
# include <unistd.h>
#endif
#define CAT_IP4_LOOPBACK "127.0.0.1"
#define CAT_IP6_LOOPBACK "::1"
namespace cat {
//// Data Types
#if defined(CAT_OS_WINDOWS)
typedef SOCKET Socket;
CAT_INLINE bool CloseSocket(Socket s) { return !closesocket(s); }
#else
typedef int Socket;
static const Socket INVALID_SOCKET = -1;
static const int SOCKET_ERROR = -1;
CAT_INLINE bool CloseSocket(Socket s) { return !close(s); }
#endif
typedef u16 Port;
#pragma pack(push)
#pragma pack(1)
// Wrapper for IPv4 and IPv6 addresses
class NetAddr
{
union
{
u8 v6_bytes[16];
u16 v6_words[8];
u64 v6[2];
struct {
u32 v4;
u32 v4_padding[3];
};
} _ip; // Network order
union
{
u32 _valid;
struct {
Port _port; // Host order
u16 _family; // Host order
};
};
public:
static const int IP4_BYTES = 4;
static const int IP6_BYTES = 16;
typedef sockaddr_in6 SockAddr;
public:
CAT_INLINE NetAddr() {}
NetAddr(const char *ip_str, Port port = 0);
NetAddr(const sockaddr_in6 &addr);
NetAddr(const sockaddr_in &addr);
NetAddr(const sockaddr *addr);
NetAddr(int a, int b, int c, int d, Port port = 0);
public:
NetAddr(const NetAddr &addr);
NetAddr &operator=(const NetAddr &addr);
public:
bool Wrap(const sockaddr_in6 &addr);
bool Wrap(const sockaddr_in &addr);
bool Wrap(const sockaddr *addr);
public:
// Promote an IPv4 address to an IPv6 address if needed
bool PromoteTo6();
// Demote an IPv6 address to an IPv4 address if possible,
// otherwise marks address as invalid and returns false
bool DemoteTo4();
CAT_INLINE bool Convert(bool To6) { if (To6) return PromoteTo6(); else return DemoteTo4(); }
public:
CAT_INLINE bool Valid() const { return _valid != 0; }
CAT_INLINE bool Is6() const { return _family == AF_INET6; }
CAT_INLINE const u32 GetIP4() const { return _ip.v4; }
CAT_INLINE const u64 *GetIP6() const { return _ip.v6; }
CAT_INLINE Port GetPort() const { return _port; }
CAT_INLINE void SetPort(Port port) { _port = port; }
// Mark the address as invalid
CAT_INLINE void Invalidate() { _valid = 0; }
public:
bool EqualsIPOnly(const NetAddr &addr) const;
bool operator==(const NetAddr &addr) const;
bool operator!=(const NetAddr &addr) const;
public:
// To validate external input; don't want clients connecting
// to their local network instead of the actual game server.
bool IsInternetRoutable();
// Returns true if the address is routable on local network or Internet.
// Returns false if the address is IPv4 multicast, loopback, or weird.
bool IsRoutable();
public:
bool SetFromString(const char *ip_str, Port port = 0);
std::string IPToString() const;
bool SetFromRawIP(const u8 *ip_binary, int bytes);
bool SetFromDotDecimals(int a, int b, int c, int d, Port port = 0);
public:
bool Unwrap(SockAddr &addr, int &addr_len, bool PromoteToIP6 = false) const;
};
#pragma pack(pop)
//// Helper Functions
// Run startup and cleanup functions needed under some OS
bool StartupSockets(); // returns false on error
void CleanupSockets();
// inout_OnlyIPv4: Indicates that only IPv4 is requested by caller
// Sets OnlyIPv4 if IPv6 will be unsupported
// Returns true on success
bool CreateSocket(int type, int protocol, bool SupportIPv4, Socket &out_s, bool &inout_OnlyIPv4);
// Returns true on success
bool NetBind(Socket s, Port port, bool OnlyIPv4);
// Returns 0 on failure
Port GetBoundPort(Socket s);
//// Error Codes
// Returns a string describing the last error from Winsock2 API
std::string SocketGetLastErrorString();
std::string SocketGetErrorString(int code);
} // namespace cat
#endif // CAT_SOCKETS_HPP

View File

@ -0,0 +1,116 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SPHYNX_CLIENT_HPP
#define CAT_SPHYNX_CLIENT_HPP
#include <cat/net/SphynxTransport.hpp>
#include <cat/crypt/tunnel/KeyAgreementInitiator.hpp>
#include <cat/threads/Thread.hpp>
#include <cat/threads/WaitableFlag.hpp>
namespace cat {
namespace sphynx {
//// sphynx::Client
class Client : Thread, public UDPEndpoint, public Transport
{
static const int HANDSHAKE_TICK_RATE = 100; // milliseconds
static const int INITIAL_HELLO_POST_INTERVAL = 200; // milliseconds
static const int CONNECT_TIMEOUT = 6000; // milliseconds
static const u32 MTU_PROBE_INTERVAL = 8000; // 8 seconds
static const int CLIENT_THREAD_KILL_TIMEOUT = 10000; // 10 seconds
static const int SESSION_KEY_BYTES = 32;
char _session_key[SESSION_KEY_BYTES];
KeyAgreementInitiator _key_agreement_initiator;
u8 _server_public_key[PUBLIC_KEY_BYTES];
u8 _cached_challenge[CHALLENGE_BYTES];
WaitableFlag _kill_flag;
protected:
u32 _last_send_mstsc;
NetAddr _server_addr;
bool _connected;
AuthenticatedEncryption _auth_enc;
// Last time a packet was received from the server -- for disconnect timeouts
u32 _last_recv_tsc;
public:
Client();
virtual ~Client();
bool SetServerKey(ThreadPoolLocalStorage *tls, const void *server_key, int key_bytes, const char *session_key);
bool Connect(const char *hostname, Port port);
bool Connect(const NetAddr &addr);
void Disconnect();
protected:
bool IsConnected() { return _connected; }
virtual void OnClose() = 0;
virtual void OnConnectFail() = 0;
virtual void OnConnect(ThreadPoolLocalStorage *tls) = 0;
virtual void OnTimestampDeltaUpdate(u32 rtt, s32 delta) {}
virtual void OnMessage(ThreadPoolLocalStorage *tls, BufferStream msg, u32 bytes) = 0;
virtual void OnDisconnect() = 0;
virtual void OnTick(ThreadPoolLocalStorage *tls, u32 now) = 0;
private:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
virtual void OnWrite(u32 bytes);
private:
bool PostHello();
void OnUnreachable(const NetAddr &src);
// Return false to remove resolve from cache
bool OnResolve(const char *hostname, const NetAddr *array, int array_length);
virtual bool PostPacket(u8 *buffer, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes);
private:
bool ThreadFunction(void *param);
};
} // namespace sphynx
} // namespace cat
#endif // CAT_SPHYNX_CLIENT_HPP

View File

@ -0,0 +1,706 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SPHYNX_COLLEXION_HPP
#define CAT_SPHYNX_COLLEXION_HPP
#include <cat/threads/Mutex.hpp>
#include <cat/threads/RegionAllocator.hpp>
#include <cat/net/SphynxTransport.hpp>
namespace cat {
namespace sphynx {
/*
The purpose of sphynx::Collexion and sphynx::CollexionIterator is to
store lists of Connexion object references and iterate through them.
Since the number of clients may be in the thousands, I feel it is
important to scale effectively. So in the Collexion data structure,
insertion and removal are O(1) operations. Also locks should be held
for the smallest amount of time possible, so I have taken care to make
locks short and reduce the amount of locking. For example, the iterator
caches large blocks of data instead of locking for each iteration.
The design is optimized for cache usage, re-using common code to benefit
from code cache and allocating and accessing table entries on cache line
boundaries to double memory performance over a naive approach.
*/
//// sphynx::Collexion
template<class T>
class CollexionIterator;
template<class T>
struct CollexionElement
{
// Number of active enumerators using this element
// If references are held it cannot be deleted
// so the KILL flag is set on the 'next' member and
// the final enumerator to reduce the reference count
// to zero is responsible for removal.
u32 refcnt;
// Bitfield:
// 1 bit: COLLISION FLAG
// 1 bit: KILL FLAG
// 30 bits: Table index to next element in list + 1
u32 next;
// Data at this table element
T *conn;
};
struct CollexionElement2
{
// Previous table element in list + 1
u32 last;
// Hash of data pointer from main entry (so it doesn't need to be recalculated during growth)
u32 hash;
};
template<class T>
class Collexion
{
static const u32 COLLIDE_MASK = 0x80000000;
static const u32 KILL_MASK = 0x40000000;
static const u32 NEXT_MASK = 0x3fffffff;
static const u32 MIN_ALLOCATED = 32;
private:
// Number of used table elements
u32 _used;
// Number of allocated table elements
u32 _allocated;
// First table index in list of active elements
u32 _first;
// Primary table
CollexionElement<T> *_table;
// Secondary table, split off so that primary table elements will
// fit on a cache line. Contains data that is only accessed rarely.
CollexionElement2 *_table2;
// Table lock
Mutex _lock;
protected:
// Attempt to double size of hash table (does not hold lock)
bool DoubleTable();
// Hash a pointer to a 32-bit table key
static CAT_INLINE u32 HashPtr(T *ptr)
{
u64 key = 0xBADdecafDEADbeef;
#if defined(CAT_WORD_64)
key ^= *(u64*)&ptr;
#else
key ^= *(u32*)&ptr;
#endif
key = (~key) + (key << 18);
key = key ^ (key >> 31);
key = key * 21;
key = key ^ (key >> 11);
key = key + (key << 6);
key = key ^ (key >> 22);
return (u32)key;
}
// Common functions shared by interface for good code cache usage:
// Unlink a table key
void Unlink(u32 key);
// Fill an iterator with the next set of data
// Returns false if no data remains to fill
void Fill(CollexionIterator<T> &iter, u32 first);
// Find a hash table key based on data
u32 Find(T *conn);
public:
// Ctor zeros everything
Collexion()
{
_first = 0;
_used = 0;
_allocated = 0;
_table = 0;
_table2 = 0;
}
// Dtor releases dangling memory
~Collexion();
// Returns true if table is empty
CAT_INLINE bool IsEmpty() { return _used == 0; }
// Insert Connexion object, return false if already present or out of memory
bool Insert(T *conn);
// Remove Connexion object from list if it exists
bool Remove(T *conn);
// Begin iterating through list
void Begin(CollexionIterator<T> &iter);
// Iterate
void Next(CollexionIterator<T> &iter, bool refill = true);
};
//// sphynx::CollexionIterator
template<class T>
class CollexionIterator
{
friend class Collexion<T>;
static const u32 MAX_CACHE = 256;
// Parent Collexion object
Collexion<T> *_parent;
// First and last hash table indices in parent
u32 _first, _last;
// Stores the size of the parent when snapshot was taken,
// will invalidate _first and _last if table changed size
u32 _prev_allocated;
// Connexion object cache, to avoid locking and unlocking a lot
T *_cache[MAX_CACHE];
// Offset into cache and total elements in cache
// Will grab another cache once offset reaches total
u32 _offset, _total;
public:
// Smart pointer -style accessors
CAT_INLINE T *Get() throw() { return _cache[_offset]; }
CAT_INLINE T *operator->() throw() { return _cache[_offset]; }
CAT_INLINE T &operator*() throw() { return *_cache[_offset]; }
CAT_INLINE operator T*() { return _cache[_offset]; }
public:
// Ctor: Grabs first cache of Connexions
CollexionIterator(Collexion<T> &begin);
// Dtor: Calls Release()
~CollexionIterator();
// Iterate to next Connexion object in list
CollexionIterator &operator++();
// Releases reference to any outstanding Connexions
void Release();
};
//// sphynx::Collexion
template<class T>
bool Collexion<T>::DoubleTable()
{
u32 new_allocated = _allocated << 1;
if (new_allocated < MIN_ALLOCATED) new_allocated = MIN_ALLOCATED;
// Allocate secondary table
u32 new_bytes2 = sizeof(CollexionElement2) * new_allocated;
CollexionElement2 *new_table2 = reinterpret_cast<CollexionElement2*>(
RegionAllocator::ii->Acquire(new_bytes2) );
if (!new_table2) return false;
// Allocate primary table
u32 new_bytes = sizeof(CollexionElement<T>) * new_allocated;
CollexionElement<T> *new_table = reinterpret_cast<CollexionElement<T> *>(
RegionAllocator::ii->Acquire(new_bytes) );
if (!new_table)
{
RegionAllocator::ii->Release(new_table2);
return false;
}
CAT_CLR(new_table, new_bytes);
u32 new_first = 0;
if (_table && _table2)
{
// For each entry in the old table,
u32 ii = _first, mask = _allocated - 1;
while (ii)
{
--ii;
CollexionElement<T> *oe = &_table[ii];
u32 hash = _table2[ii].hash;
u32 key = hash & mask;
// While collisions occur,
while (new_table[key].conn)
{
// Mark collision
new_table[key].next |= COLLIDE_MASK;
// Walk collision list
key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
}
// Fill new table element
new_table[key].conn = oe->conn;
new_table2[key].hash = hash;
// new_table[key].refcnt is already zero
// Link new element to new list
if (new_first)
{
new_table[key].next |= new_first;
new_table2[new_first - 1].last = key;
}
// new_table[key].next is already zero so no need to zero it here
new_first = key + 1;
// Get next old table entry
ii = oe->next & NEXT_MASK;
}
// Zero head->last
new_table2[new_first - 1].last = 0;
}
// Resulting linked list starting with _first-1 will extend until e->next == 0
if (_table2) RegionAllocator::ii->Release(_table2);
if (_table) RegionAllocator::ii->Release(_table);
_table = new_table;
_table2 = new_table2;
_allocated = new_allocated;
_first = new_first;
return true;
}
template<class T>
Collexion<T>::~Collexion()
{
if (_table2)
{
RegionAllocator::ii->Release(_table2);
}
// If table doesn't exist, return
if (!_table) return;
// For each allocated table entry,
for (u32 ii = 0; ii < _allocated; ++ii)
{
// Get Connexion object
T *conn = _table[ii].conn;
// If object is valid, release it
if (conn) conn->ReleaseRef();
}
// Release table memory
RegionAllocator::ii->Release(_table);
}
template<class T>
bool Collexion<T>::Insert(T *conn)
{
u32 hash = HashPtr(conn);
conn->AddRef();
AutoMutex lock(_lock);
// If more than half of the table will be used,
if (_used >= (_allocated >> 1))
{
// Double the size of the table (O(1) allocation pattern)
// Growing pains are softened by careful design
if (!DoubleTable())
{
// On growth failure, return false
lock.Release();
conn->ReleaseRef();
return false;
}
}
// Mask off high bits to make table key from hash
u32 mask = _allocated - 1;
u32 key = hash & mask;
// While empty table entry not found,
while (_table[key].conn)
{
// If Connexion object is already in the table,
if (_table[key].conn == conn)
{
// Return false on duplicate
lock.Release();
conn->ReleaseRef();
return false;
}
// Mark as a collision
_table[key].next |= COLLIDE_MASK;
// Walk collision list
key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
}
// Fill new element
_table[key].conn = conn;
_table[key].refcnt = 0;
_table2[key].hash = hash;
_table2[key].last = 0;
// Link new element to front of list
if (_first) _table2[_first - 1].last = key + 1;
_table[key].next = (_table[key].next & COLLIDE_MASK) | _first;
_first = key + 1;
++_used;
return true;
}
template<class T>
void Collexion<T>::Unlink(u32 key)
{
// Clear reference
_table[key].conn = 0;
// Unlink from active list
u32 next = _table[key].next & NEXT_MASK;
u32 last = _table2[key].last;
if (last) _table[last-1].next = (_table[last-1].next & ~NEXT_MASK) | next;
else _first = next;
if (next) _table2[next-1].last = last;
// If this key was a leaf on a collision wind,
if (!(_table[key].next & COLLIDE_MASK))
{
u32 mask = _allocated - 1;
do
{
// Go backwards through the collision list one step
key = ((key + COLLISION_INCRINVERSE) * COLLISION_MULTINVERSE) & mask;
// Stop where collision list stops
if (!(_table[key].next & COLLIDE_MASK))
break;
// Turn off collision key for previous entry
_table[key].next &= ~COLLIDE_MASK;
} while (!_table[key].conn);
}
// Update number of used elements
--_used;
}
template<class T>
bool Collexion<T>::Remove(T *conn)
{
u32 hash = HashPtr(conn);
AutoMutex lock(_lock);
// If table doesn't exist,
if (!_allocated) return false;
// Mask off high bits to make table key from hash
u32 mask = _allocated - 1;
u32 key = hash & mask;
// While target table entry not found,
for (;;)
{
// If target was found,
if (_table[key].conn == conn)
{
if (_table[key].refcnt)
{
// Mark it killed so iterator can clean it up when it's finished
_table[key].next |= KILL_MASK;
}
else
{
Unlink(key);
lock.Release();
conn->ReleaseRef();
}
// Return success
return true;
}
if (!(_table[key].next & COLLIDE_MASK))
{
break; // End of collision list
}
// Walk collision list
key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
}
// Return failure: not found
return false;
}
template<class T>
void Collexion<T>::Fill(CollexionIterator<T> &iter, u32 first)
{
u32 key = first;
// Find first list element that does not want to die
while (key && (_table[key-1].next & KILL_MASK))
{
// Go to next
key = _table[key-1].next & NEXT_MASK;
}
iter._offset = 0;
// If no elements in table,
if (!key)
{
// Return empty set
iter._cache[0] = 0;
iter._total = 0;
iter._first = 0;
iter._last = 0;
return;
}
// Remember size of hash table in case it grows before iterator is done
iter._prev_allocated = _allocated;
// Remember first key for next iteration
iter._first = key;
// For each of the first MAX_CACHE elements in the table, copy the data pointer to cache
u32 ii = 0, final = 0;
do
{
// If element does not want to die,
if (!(_table[key-1].next & KILL_MASK))
{
// Copy data pointer
iter._cache[ii] = _table[key-1].conn;
// Increment reference count for element
_table[key-1].refcnt++;
// Remember key as the next iteration starting point
final = key;
// Check if copy is complete
if (++ii >= CollexionIterator<T>::MAX_CACHE) break;
}
// Go to next key
key = _table[key-1].next & NEXT_MASK;
} while (key);
// Record number of elements written
iter._total = ii;
// Remember next key for next iteration
iter._last = final;
}
template<class T>
void Collexion<T>::Begin(CollexionIterator<T> &iter)
{
iter._parent = this;
AutoMutex lock(_lock);
Fill(iter, _first);
}
// Find a hash table key based on data
template<class T>
u32 Collexion<T>::Find(T *conn)
{
u32 mask = _allocated - 1;
u32 key = HashPtr(conn) & mask;
// Find the object in the collision list starting at the expected location
while (_table[key].conn != conn)
{
// If at the end of the collision list,
if (!(_table[key].next & COLLIDE_MASK))
break; // Should never happen
// Walk collision list
key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
}
return key;
}
template<class T>
void Collexion<T>::Next(CollexionIterator<T> &iter, bool refill)
{
T *release_list[CollexionIterator<T>::MAX_CACHE];
u32 release_ii = 0;
u32 key = iter._first;
u32 last = iter._last;
// If iteration is done,
if (!key) return;
AutoMutex lock(_lock);
// If hash table changed size (rare),
if (iter._prev_allocated != _allocated)
{
// iter._first and iter._last are invalid
// ...but we can find them again based on the cached pointers!
key = Find(iter._cache[0]) + 1;
last = Find(iter._cache[iter._total - 1]) + 1;
}
last = _table[last-1].next & NEXT_MASK;
// Release any table elements that want to die now
do
{
u32 flags = _table[key-1].next;
// If reference count is now zero for this element,
if (0 == --_table[key-1].refcnt)
{
// If element wants to die,
if (flags & KILL_MASK)
{
// Prepare to release data after lock is released
release_list[release_ii++] = _table[key-1].conn;
Unlink(key-1);
}
}
key = flags & NEXT_MASK;
} while (key != last);
// Fill iterator starting with next key
if (refill) Fill(iter, key);
lock.Release();
if (!refill)
{
// Return empty set
iter._cache[0] = 0;
iter._total = 0;
iter._offset = 0;
iter._first = 0;
iter._last = 0;
}
// Release data awaiting destruction
for (u32 ii = 0; ii < release_ii; ++ii)
{
release_list[ii]->ReleaseRef();
}
}
//// sphynx::CollexionIterator
template<class T>
CollexionIterator<T>::CollexionIterator(Collexion<T> &begin)
{
begin.Begin(*this);
}
template<class T>
CollexionIterator<T>::~CollexionIterator()
{
Release();
}
template<class T>
CollexionIterator<T> &CollexionIterator<T>::operator++()
{
if (++_offset >= _total)
{
_parent->Next(*this, true);
}
return *this;
}
template<class T>
void CollexionIterator<T>::Release()
{
if (_parent)
{
_parent->Next(*this, false);
_parent = 0;
}
}
} // namespace sphynx
} // namespace cat
#endif // CAT_SPHYNX_COLLEXION_HPP

View File

@ -0,0 +1,300 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SPHYNX_SERVER_HPP
#define CAT_SPHYNX_SERVER_HPP
#include <cat/net/SphynxTransport.hpp>
#include <cat/threads/RWLock.hpp>
#include <cat/threads/Thread.hpp>
#include <cat/threads/WaitableFlag.hpp>
#include <cat/net/SphynxCollexion.hpp>
#include <cat/crypt/cookie/CookieJar.hpp>
#include <cat/crypt/tunnel/KeyAgreementResponder.hpp>
namespace cat {
namespace sphynx {
/*
Designed for server hardware with many processors.
In order to handle many users, the Sphynx server opens up a single UDP port
to accept new connections, and several other UDP data ports for data.
For retransmissions and detecting link loss due to timeout, the server runs
several additional threads that wake up periodically to perform timed tasks.
Server uses thread pool to receive packets on connect and worker ports,
meaning that packets are processed by any free CPU as soon as they arrive.
Sphynx Server
UDP Hello Port [1]
+ In case this thread spins constantly, only use one CPU for new
connections since in-game experience is more important than login
+ Assigns users to a data port after handshake completes
UDP Data Ports [4 * (CPU Count)]
+ Spread users evenly across several ports since
only one packet can be processed from a single port at a time
+ Any free CPU will process incoming packets as fast as possible
ServerTimer threads [(CPU Count) / 2]
+ In case these threads spin constantly, they only consume
half of the CPU resources available
+ Wake up every X milliseconds according to Transport::TICK_RATE
+ Detect link loss due to silence timeout
+ Update transport layer
+ Retransmit lost messages
+ Re-evaluate bandwidth and transmit queued messages
*/
//// sphynx::Connexion
// Derive from sphynx::Connexion and sphynx::Server to define server behavior
class Connexion : public Transport, public ThreadRefObject
{
friend class Server;
friend class Map;
friend class ServerWorker;
friend class ServerTimer;
public:
Connexion();
virtual ~Connexion() {}
private:
volatile u32 _destroyed;
u32 _key; // Map hash table index, unique for each active connection
Connexion *_next_delete;
ServerWorker *_server_worker;
u8 _first_challenge[64]; // First challenge seen from this client address
u8 _cached_answer[128]; // Cached answer to this first challenge, to avoid eating server CPU time
private:
// Return false to destroy this object
bool Tick(ThreadPoolLocalStorage *tls, u32 now);
void OnRawData(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes);
virtual bool PostPacket(u8 *buffer, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes);
public:
CAT_INLINE bool IsValid() { return _destroyed == 0; }
CAT_INLINE u32 GetKey() { return _key; }
void Destroy();
protected:
NetAddr _client_addr;
// Last time a packet was received from this user -- for disconnect timeouts
u32 _last_recv_tsc;
bool _seen_encrypted;
AuthenticatedEncryption _auth_enc;
protected:
virtual void OnConnect(ThreadPoolLocalStorage *tls) = 0;
virtual void OnDestroy() = 0;
virtual void OnTick(ThreadPoolLocalStorage *tls, u32 now) = 0;
};
//// sphynx::Map
class Map
{
protected:
CAT_INLINE u32 hash_addr(const NetAddr &addr, u32 salt);
public:
struct Slot
{
Connexion *connection;
bool collision;
Slot *next;
};
protected:
u32 _hash_salt;
CAT_ALIGNED(16) Slot _table[HASH_TABLE_SIZE];
RWLock _table_lock;
public:
Map();
virtual ~Map();
// Lookup client by address
Connexion *Lookup(const NetAddr &addr);
// Lookup client by key
Connexion *Lookup(u32 key);
// May return false if network address in Connexion is already in the map.
// This averts a potential race condition but should never happen during
// normal operation, so the Connexion allocation by caller won't be wasted.
bool Insert(Connexion *conn);
// Destroy a list described by the 'next' member of Slot
void DestroyList(Map::Slot *kill_list);
void Tick(ThreadPoolLocalStorage *tls);
};
//// sphynx::ServerWorker
class ServerWorker : public UDPEndpoint
{
friend class Map;
protected:
Map *_conn_map;
ServerTimer *_server_timer;
protected:
volatile u32 _session_count;
public:
ServerWorker(Map *conn_map, ServerTimer *server_timer);
virtual ~ServerWorker();
void IncrementPopulation();
void DecrementPopulation();
u32 GetPopulation() { return _session_count; }
protected:
void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
void OnWrite(u32 bytes) {}
void OnClose();
};
//// sphynx::ServerTimer
class ServerTimer : Thread
{
protected:
Map *_conn_map;
protected:
ServerWorker **_workers;
int _worker_count;
protected:
Map::Slot *_insert_head;
Mutex _insert_lock;
protected:
Map::Slot *_active_head;
public:
ServerTimer(Map *conn_map, ServerWorker **workers, int worker_count);
virtual ~ServerTimer();
CAT_INLINE bool Valid() { return _worker_count > 0; }
public:
void InsertSlot(Map::Slot *slot);
protected:
CAT_INLINE void Tick(ThreadPoolLocalStorage *tls);
bool ThreadFunction(void *param);
protected:
static const int TIMER_THREAD_KILL_TIMEOUT = 10000;
WaitableFlag _kill_flag;
};
//// sphynx::Server
class Server : public UDPEndpoint
{
public:
Server();
virtual ~Server();
bool StartServer(ThreadPoolLocalStorage *tls, Port port, u8 *public_key, int public_bytes, u8 *private_key, int private_bytes, const char *session_key);
u32 GetTotalPopulation();
static bool GenerateKeyPair(ThreadPoolLocalStorage *tls, const char *public_key_file,
const char *private_key_file, u8 *public_key,
int public_bytes, u8 *private_key, int private_bytes);
private:
static const int SESSION_KEY_BYTES = 32;
char _session_key[SESSION_KEY_BYTES];
Port _server_port;
Map _conn_map;
CookieJar _cookie_jar;
KeyAgreementResponder _key_agreement_responder;
u8 _public_key[PUBLIC_KEY_BYTES];
static const int WORKER_LIMIT = 32; // Maximum number of workers
ServerWorker **_workers;
int _worker_count;
ServerTimer **_timers;
int _timer_count;
private:
ServerWorker *FindLeastPopulatedPort();
void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &src, u8 *data, u32 bytes);
void OnWrite(u32 bytes);
void OnClose();
protected:
// Must return a new instance of your Connexion derivation
virtual Connexion *NewConnexion() = 0;
// IP address filter: Return true to allow the connection to be made
virtual bool AcceptNewConnexion(const NetAddr &src) = 0;
// Lookup client by key
Connexion *Lookup(u32 key);
};
} // namespace sphynx
} // namespace cat
#endif // CAT_SPHYNX_SERVER_HPP

View File

@ -0,0 +1,455 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SPHYNX_TRANSPORT_HPP
#define CAT_SPHYNX_TRANSPORT_HPP
#include <cat/net/ThreadPoolSockets.hpp>
#include <cat/threads/Mutex.hpp>
#include <cat/crypt/tunnel/AuthenticatedEncryption.hpp>
#include <cat/parse/BufferStream.hpp>
namespace cat {
namespace sphynx {
/*
Based on what I have envisioned needing for my game, the following protocol is being
used after spending weeks looking at alternatives and false-starts. It provides signaled
disconnects, fragmentation, MTU discovery, time synchronization, three reliable ordered
streams, one unordered reliable stream, and unreliable delivery.
The Transport object that implements a sender/receiver in the protocol requires 276 bytes
of memory per server connection, plus 32 bytes per message fragment in flight, plus buffers
for queued send/recv packets, and it keeps two mutexes for thread-safety. A lockless memory
allocator is used to allocate all buffers except the fragmented message reassembly buffer.
Packet format on top of UDP header:
E { HDR(2 bytes)|ACK-ID(3 bytes)|DATA || ... || MAC(8 bytes) } || IV(3 bytes)
E: ChaCha-12 stream cipher.
IV: Initialization vector used by security layer (Randomly initialized).
MAC: Message authentication code used by security layer (HMAC-MD5).
HDR|ACK-ID|DATA: A message block inside the datagram. The HDR and ACK-ID
fields employ compression to use as little as 1 byte together.
Each message follows the same format. A two-byte header followed by data:
--- Message Header (16 bits) ---
0 1 2 3 4 5 6 7 8 9 a b c d e f
<-- LSB ----------------- MSB -->
| BHI |I|R| SOP | BLO |
---------------------------------
DATA_BYTES: BHI | BLO = Number of bytes in data part of message.
If BHI = "111"b, the BLO byte is omitted and the receiver can
assume that the rest of the packet contains just one message.
I: 1=Followed by ACK-ID field. 0=ACK-ID is one higher than the last.
R: 1=Reliable. 0=Unreliable.
SOP: Super opcodes:
0=Data (reliable or unreliable)
1=Fragment (reliable)
2=ACK (unreliable)
3=MTU Probe (unreliable)
4=MTU Set (unordered reliable)
5=Time Ping (unreliable)
6=Time Pong (unreliable)
7=Disconnect (unreliable)
When the I bit is set, the data part is preceded by an ACK-ID,
which is then applied to all following reliable messages.
This additional size is NOT accounted for in the DATA_BYTES field.
When the FRAG opcode used for the first time in an ordered stream,
the data part begins with a 16-bit Fragment Header.
This additional size IS accounted for in the DATA_BYTES field.
Often times there is just one message in a datagram. To compress
the header in this case, when BHI = 7 the receiver assumes that
only one message is in the packet and uses the payload length to
determine the length of the single message. BLO byte is omitted.
This *could* also be used on the final message in a cluster,
but it cannot be done without a memcpy to cover up the extra
header byte, which I don't consider worthwhile...
------------- ACK-ID Field (24 bits) ------------
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7
<-- LSB --------------------------------- MSB -->
| S | IDA (5) |C| IDB (7) |C| IDC (8) |
-------------------------------------------------
C: 1=Continues to next byte.
S: 0=Unordered stream, 1-3: Ordered streams.
ID: IDC | IDB | IDA (20 bits)
On retransmission, the ACK-ID field uses no compression
since the receiver state cannot be determined.
--- Fragment Header (16 bits) ---
0 1 2 3 4 5 6 7 8 9 a b c d e f
<-- LSB ----------------- MSB -->
| TOTAL_BYTES(16) |
---------------------------------
TOTAL_BYTES: Total bytes in data part of fragmented message,
not including this header.
*/
/*
ACK message format:
Header: I=0, R=0, SOP=SOP_ACK
Data: ROLLUP(3) || RANGE1 || RANGE2 || ... ROLLUP(3) || RANGE1 || RANGE2 || ...
ROLLUP = Next expected ACK-ID. Acknowledges every ID before this one.
RANGE1:
START || END
START = First inclusive ACK-ID in a range to acknowledge.
END = Final inclusive ACK-ID in a range to acknowledge.
Negative acknowledgment can be inferred from the holes in the RANGEs.
------------ ROLLUP Field (24 bits) -------------
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7
<-- LSB --------------------------------- MSB -->
|1| S | IDA (5) | IDB (8) | IDC (8) |
-------------------------------------------------
1: Indicates start of ROLLUP field.
S: 0=Unordered stream, 1-3: Ordered streams.
ID: IDC | IDB | IDA (21 bits)
ROLLUP is always 3 bytes since we cannot tell how far ahead the remote host is now.
--------- RANGE START Field (24 bits) -----------
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7
<-- LSB --------------------------------- MSB -->
|0|E| IDA (5) |C| IDB (7) |C| IDC (8) |
-------------------------------------------------
0: Indicates start of RANGE field.
C: 1=Continues to next byte.
E: 1=Has end field. 0=One ID in range.
ID: IDC | IDB | IDA (20 bits) + last ack id in message
---------- RANGE END Field (24 bits) ------------
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7
<-- LSB --------------------------------- MSB -->
| IDA (7) |C| IDB (7) |C| IDC (8) |
-------------------------------------------------
C: 1=Continues to next byte.
ID: IDC | IDB | IDA (22 bits) + START.ID
*/
class Connexion;
class Map;
class Server;
class ServerWorker;
class ServerTimer;
class Client;
class Transport;
#if defined(CAT_WORD_32)
#define CAT_PACK_TRANSPORT_STATE_STRUCTURES /* For 32-bit version, this allows fragments to fit in 32 bytes */
#else // 64-bit version:
//#define CAT_PACK_TRANSPORT_STATE_STRUCTURES /* No advantage for 64-bit version */
#endif
//#define CAT_TRANSPORT_DEBUG_LOGGING /* Enables info messages on console */
#define CAT_VERBOSE_VALIDATION /* Enables input error messages on console */
// Protocol constants
static const u32 PROTOCOL_MAGIC = 0xC47D0001;
static const int PUBLIC_KEY_BYTES = 64;
static const int PRIVATE_KEY_BYTES = 32;
static const int CHALLENGE_BYTES = PUBLIC_KEY_BYTES;
static const int ANSWER_BYTES = PUBLIC_KEY_BYTES*2;
static const int HASH_TABLE_SIZE = 32768; // Power-of-2
static const int HASH_TABLE_MASK = HASH_TABLE_SIZE - 1;
static const int MAX_POPULATION = HASH_TABLE_SIZE / 2;
// (multiplier-1) divisible by all prime factors of table size
// (multiplier-1) is a multiple of 4 if table size is a multiple of 4
// These constants are from Press, Teukolsky, Vetterling and Flannery's
// "Numerical Recipes in FORTRAN: The Art of Scientific Computing"
static const u32 COLLISION_MULTIPLIER = 71*5861 * 4 + 1;
static const u32 COLLISION_INCREMENTER = 1013904223;
// If multiplier changes, this needs to be recalculated (multiplicative inverse of above)
static const u32 COLLISION_MULTINVERSE = 4276115653;
static const u32 COLLISION_INCRINVERSE = 0 - COLLISION_INCREMENTER;
// Handshake types
enum HandshakeType
{
C2S_HELLO,
S2C_COOKIE,
C2S_CHALLENGE,
S2C_ANSWER,
S2C_ERROR
};
// Handshake errors
enum HandshakeError
{
ERR_SERVER_FULL
};
// Stream modes
enum StreamMode
{
STREAM_UNORDERED = 0, // Reliable, unordered stream 0
STREAM_1 = 1, // Reliable, ordered stream 1
STREAM_2 = 2, // Reliable, ordered stream 2
STREAM_3 = 3 // Reliable, ordered stream 3
};
// Super Opcodes
enum SuperOpcode
{
SOP_DATA, // 0=Data (reliable or unreliable)
SOP_FRAG, // 1=Fragment (reliable)
SOP_ACK, // 2=ACK (unreliable)
SOP_MTU_PROBE, // 3=MTU Probe (unreliable)
SOP_MTU_SET, // 4=MTU Set (unordered reliable)
SOP_TIME_PING, // 5=Time Ping (unreliable)
SOP_TIME_PONG, // 6=Time Pong (unreliable)
SOP_DISCO, // 7=Disconnect (unreliable)
};
//// sphynx::Transport
#if defined(CAT_PACK_TRANSPORT_STATE_STRUCTURES)
#pragma pack(push)
#pragma pack(1)
#endif // CAT_PACK_TRANSPORT_STATE_STRUCTURES
// Receive state: Receive queue
struct RecvQueue
{
static const u32 FRAG_FLAG = 0x80000000;
static const u32 BYTE_MASK = 0x7fffffff;
RecvQueue *next; // Next in queue
RecvQueue *prev; // Previous in queue
u32 id; // Acknowledgment id
u32 bytes; // High bit: Fragment?
// Message contents follow
};
// Send state: Send queue
struct SendQueue
{
SendQueue *next; // Next in queue
SendQueue *prev; // Previous in queue
u32 ts_firstsend; // Millisecond-resolution timestamp when it was first sent
u32 ts_lastsend; // Millisecond-resolution timestamp when it was last sent
union
{
u32 sent_bytes; // In send queue: Number of sent bytes while fragmenting a large message
u32 id; // In sent list: Acknowledgment id
};
u16 bytes; // Data bytes
u16 frag_count; // Number of fragments remaining to be delivered
u16 sop; // Super opcode of message
// Message contents follow
};
struct SendFrag : public SendQueue
{
SendQueue *full_data; // Object containing message data
u16 offset; // Fragment data offset
};
// Temporary send node structure, nestled in the encryption overhead of outgoing packets
struct TempSendNode // Size <= 11 bytes = AuthenticatedEncryption::OVERHEAD_BYTES
{
static const u32 SINGLE_FLAG = 0x8000;
static const u32 BYTE_MASK = 0x7fff;
TempSendNode *next;
u16 negative_offset; // Number of bytes before this structure, and single flag
};
#if defined(CAT_PACK_TRANSPORT_STATE_STRUCTURES)
#pragma pack(pop)
#endif // CAT_PACK_TRANSPORT_STATE_STRUCTURES
class Transport
{
protected:
static const u8 BHI_MASK = 7;
static const u8 BHI_SINGLE_MESSAGE = 7;
static const u8 I_MASK = 1 << 3;
static const u8 R_MASK = 1 << 4;
static const u32 SOP_SHIFT = 5;
static const u32 NUM_STREAMS = 4; // Number of reliable streams
static const u32 MIN_RTT = 50; // Minimum milliseconds for RTT
static const int TIMEOUT_DISCONNECT = 15000; // 15 seconds
static const int SILENCE_LIMIT = 9333; // 9.333 seconds: Time silent before sending a keep-alive (0-length unordered reliable message)
static const int TICK_RATE = 20; // 20 milliseconds
static const u32 MINIMUM_MTU = 576; // Dialup
static const u32 MEDIUM_MTU = 1400; // Highspeed with unexpected overhead, maybe VPN
static const u32 MAXIMUM_MTU = 1500; // Highspeed
static const u32 IPV6_OPTIONS_BYTES = 40; // TODO: Not sure about this
static const u32 IPV6_HEADER_BYTES = 40 + IPV6_OPTIONS_BYTES;
static const u32 IPV4_OPTIONS_BYTES = 40;
static const u32 IPV4_HEADER_BYTES = 20 + IPV4_OPTIONS_BYTES;
static const u32 UDP_HEADER_BYTES = 8;
static const u32 FRAG_THRESHOLD = 32; // Fragment if FRAG_THRESHOLD bytes would be in each fragment
static const u32 MAX_MESSAGE_DATALEN = 65535-1; // Maximum number of bytes in the data part of a message (-1 for the opcode)
protected:
// Maximum transfer unit (MTU) in UDP payload bytes, excluding the IP and UDP headers and encryption overhead
u32 _max_payload_bytes;
public:
void InitializePayloadBytes(bool ip6);
protected:
// Receive state: Next expected ack id to receive
u32 _next_recv_expected_id[NUM_STREAMS];
// Receive state: Synchronization objects
volatile bool _got_reliable[NUM_STREAMS];
Mutex _recv_lock; // Just needs to protect writes OnDatagram() from messing up reads on tick
// Receive state: Fragmentation
u8 *_fragment_buffer[NUM_STREAMS]; // Buffer for accumulating fragment
u32 _fragment_length[NUM_STREAMS]; // Number of bytes in fragment buffer
u32 _fragment_offset[NUM_STREAMS]; // Current write offset in buffer
static const u32 FRAG_MIN = 0; // Min bytes for a fragmented message
static const u32 FRAG_MAX = 65535; // Max bytes for a fragmented message
// Receive state: Receive queue head
RecvQueue *_recv_queue_head[NUM_STREAMS], *_recv_queue_tail[NUM_STREAMS];
private:
void RunQueue(ThreadPoolLocalStorage *tls, u32 ack_id, u32 stream);
void QueueRecv(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes, u32 ack_id, u32 stream, u32 super_opcode);
protected:
// Send state: Synchronization objects
Mutex _send_lock;
// Send state: Next ack id to use
u32 _next_send_id[NUM_STREAMS];
// Send state: Estimated round-trip time
u32 _rtt; // milliseconds
// Send state: Last rollup ack id from remote receiver
u32 _send_next_remote_expected[NUM_STREAMS];
// Send state: Combined writes
u8 *_send_buffer;
u32 _send_buffer_bytes;
u32 _send_buffer_stream, _send_buffer_ack_id; // Used to compress ACK-ID by setting I=0 after the first reliable message
u32 _send_buffer_msg_count; // Used to compress datagrams with a single message by omitting the header's BLO field
// Queue of messages that are waiting to be sent
SendQueue *_send_queue_head[NUM_STREAMS], *_send_queue_tail[NUM_STREAMS];
// List of messages that are waiting to be acknowledged
SendQueue *_sent_list_head[NUM_STREAMS], *_sent_list_tail[NUM_STREAMS];
bool _disconnected;
private:
void TransmitQueued();
void Retransmit(u32 stream, SendQueue *node, u32 now); // Does not hold the send lock!
void WriteACK();
void OnACK(u8 *data, u32 data_bytes);
void OnMTUSet(u8 *data, u32 data_bytes);
public:
Transport();
virtual ~Transport();
public:
// ata_bytes: Length of msg_data
bool WriteUnreliable(u8 msg_opcode, const void *msg_data = 0, u32 data_bytes = 0);
bool WriteReliable(StreamMode, u8 msg_opcode, const void *msg_data = 0, u32 data_bytes = 0, SuperOpcode super_opcode = SOP_DATA);
void FlushWrite();
protected:
// Notify transport layer of disconnect to halt message processing
CAT_INLINE void TransportDisconnected() { _disconnected = true; }
CAT_INLINE bool IsDisconnected() { return _disconnected; }
void TickTransport(ThreadPoolLocalStorage *tls, u32 now);
void OnDatagram(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes);
private:
void OnFragment(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes, u32 stream);
void CombineNextWrite();
protected:
// skip_bytes: Number of bytes to skip at the start of the buffer
// buf_bytes and msg_bytes contain the skipped bytes
virtual bool PostPacket(u8 *data, u32 buf_bytes, u32 msg_bytes, u32 skip_bytes) = 0;
virtual void OnTimestampDeltaUpdate(u32 rtt, s32 delta) {}
virtual void OnMessage(ThreadPoolLocalStorage *tls, BufferStream msg, u32 bytes) = 0;
virtual void OnDisconnect() = 0;
protected:
bool PostMTUProbe(ThreadPoolLocalStorage *tls, u16 payload_bytes);
bool PostTimePing();
bool PostTimePong(u32 client_ts);
bool PostDisconnect();
};
} // namespace sphynx
} // namespace cat
#endif // CAT_SPHYNX_TRANSPORT_HPP

View File

@ -0,0 +1,95 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: Half-open connections can easily DoS the TCP server right now.
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
#include <cat/threads/ThreadPool.hpp>
#include <cat/net/Sockets.hpp>
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnexion;
class TCPClient;
class UDPEndpoint;
void ReportUnexpectedSocketError(int error);
} // namespace cat
#if defined (CAT_OS_WINDOWS) // Windows-style IOCP
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
#include <cat/net/win/TCPServer.hpp>
#include <cat/net/win/TCPConnexion.hpp>
#include <cat/net/win/TCPClient.hpp>
#include <cat/net/win/UDPEndpoint.hpp>
#elif defined(CAT_OS_LINUX) // Linux-style eventfd
#include <cat/net/linux/TCPServer.hpp>
#include <cat/net/linux/TCPConnexion.hpp>
#include <cat/net/linux/TCPClient.hpp>
#include <cat/net/linux/UDPEndpoint.hpp>
#elif defined(CAT_OS_OSX) || defined(CAT_OS_BSD) // BSD-style kevent
#include <cat/net/bsd/TCPServer.hpp>
#include <cat/net/bsd/TCPConnexion.hpp>
#include <cat/net/bsd/TCPClient.hpp>
#include <cat/net/bsd/UDPEndpoint.hpp>
#else // Fall-back
#include <cat/net/generic/TCPServer.hpp>
#include <cat/net/generic/TCPConnexion.hpp>
#include <cat/net/generic/TCPClient.hpp>
#include <cat/net/generic/UDPEndpoint.hpp>
#endif
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,348 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: Half-open connections can easily DoS the TCP server right now.
// TODO: Port to Linux(eventfd) and BSD/MacOSX(kevent)
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
#include <cat/threads/ThreadPool.hpp>
#include <cat/net/Sockets.hpp>
#if defined(CAT_MS_SOCKET_API)
# include <MSWSock.h>
# include <cat/port/WindowsInclude.hpp>
#endif
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,242 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,347 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,319 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_UDP_ENDPOINT_HPP
#define CAT_THREAD_POOL_UDP_ENDPOINT_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_UDP_ENDPOINT_HPP

View File

@ -0,0 +1,348 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: Half-open connections can easily DoS the TCP server right now.
// TODO: Port to Linux(eventfd) and BSD/MacOSX(kevent)
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
#include <cat/threads/ThreadPool.hpp>
#include <cat/net/Sockets.hpp>
#if defined(CAT_MS_SOCKET_API)
# include <MSWSock.h>
# include <cat/port/WindowsInclude.hpp>
#endif
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,242 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,347 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,319 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_UDP_ENDPOINT_HPP
#define CAT_THREAD_POOL_UDP_ENDPOINT_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_UDP_ENDPOINT_HPP

View File

@ -0,0 +1,348 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// TODO: Half-open connections can easily DoS the TCP server right now.
// TODO: Port to Linux(eventfd) and BSD/MacOSX(kevent)
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
#include <cat/threads/ThreadPool.hpp>
#include <cat/net/Sockets.hpp>
#if defined(CAT_MS_SOCKET_API)
# include <MSWSock.h>
# include <cat/port/WindowsInclude.hpp>
#endif
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,242 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,347 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_SOCKETS_HPP
#define CAT_THREAD_POOL_SOCKETS_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
/*
Thread Pool Sockets library
Provides a framework for rapidly developing TCP/UDP server and client objects
that make use of high performance APIs under various server and desktop
operating systems.
All network events are processed by a thread pool managed by ThreadPool.
*/
class TCPServer;
class TCPConnection;
class TCPClient;
class UDPEndpoint;
//// Buffer Management
// Generate a buffer to pass to Post()
u8 *GetPostBuffer(u32 bytes);
void *ResizePostBuffer(void *buffer, u32 newBytes);
// Release a buffer provided by GetPostBuffer()
// Note: Once the buffer is submitted to Post() this is unnecessary
void ReleasePostBuffer(void *buffer);
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_SOCKETS_HPP

View File

@ -0,0 +1,319 @@
/*
Copyright (c) 2009 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_THREAD_POOL_UDP_ENDPOINT_HPP
#define CAT_THREAD_POOL_UDP_ENDPOINT_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
#include <MSWSock.h>
#include <cat/port/WindowsInclude.hpp>
namespace cat {
//// Overlapped Sockets
// AcceptEx() OVERLAPPED structure
struct AcceptExOverlapped
{
TypedOverlapped tov;
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address either!
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
void Set(Socket s);
};
// WSARecvFrom() OVERLAPPED structure
struct RecvFromOverlapped
{
TypedOverlapped tov;
// Not necessarily and IPv6 address,
// but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
// data follows...
void Reset();
};
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnection() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnection;
friend class ThreadPool;
public:
TCPServer();
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(Port port = 0);
void Close();
protected:
virtual TCPConnection *InstantiateServerConnection() = 0;
private:
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
private:
bool QueueAcceptEx();
bool QueueAccepts();
void OnAcceptExComplete(int error, AcceptExOverlapped *overlapped);
};
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection();
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true iff the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
friend class ThreadPool;
public:
TCPClient();
virtual ~TCPClient();
bool ValidClient();
bool Connect(const NetAddr &remoteServerAddress);
void DisconnectServer();
bool PostToServer(void *buffer, u32 bytes);
protected:
virtual void OnConnectToServer() = 0;
virtual bool OnReadFromServer(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToServer(u32 bytes) = 0;
virtual void OnDisconnectFromServer() = 0;
private:
Socket _socket;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
bool _ipv6;
private:
bool QueueConnectEx(const NetAddr &remoteServerAddress);
void OnConnectExComplete(int error);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
void *_queueBuffer;
u32 _queueBytes;
protected:
void PostQueuedToServer();
public:
TCPClientQueued();
virtual ~TCPClientQueued();
bool PostToServer(void *buffer, u32 bytes);
};
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
friend class ThreadPool;
public:
UDPEndpoint();
virtual ~UDPEndpoint();
bool Valid();
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
void Close(); // Invalidates this object
bool Bind(Port port = 0, bool ignoreUnreachable = true);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling Post() with addr.PromoteTo6()
bool Post(const NetAddr &addr, void *data, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual void OnWrite(u32 bytes) = 0;
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
private:
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
private:
bool QueueWSARecvFrom(RecvFromOverlapped *recvOv);
void OnWSARecvFromComplete(ThreadPoolLocalStorage *tls, int error, RecvFromOverlapped *recvOv, u32 bytes);
bool QueueWSASendTo(const NetAddr &addr, TypedOverlapped *sendOv, u32 bytes);
void OnWSASendToComplete(int error, u32 bytes);
};
} // namespace cat
#endif // CAT_THREAD_POOL_UDP_ENDPOINT_HPP

View File

@ -0,0 +1,126 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_TCP_CLIENT_HPP
#define CAT_TCP_CLIENT_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
namespace cat {
/*
class TCPClient
Object that represents a TCPClient bound to a single port
ValidClient() : Returns true if the client socket is valid
Connect() : Connects to the given address
DisconnectServer() : Disconnects from the server
PostToServer() : Send a message to the server (will fail if not connected)
OnConnectToServer() : Called when connection is accepted
OnReadFromServer() : Return false to disconnect the server in response to data
OnWriteToServer() : Informs the derived class that data has been sent
OnDisconnectFromServer() : Informs the derived class that the server has disconnected
*/
class TCPClient : public ThreadRefObject
{
// Remembers if socket is IPv6 so that user-provided
// addresses can be promoted if necessary.
bool _ipv6;
Socket _socket;
volatile u32 _disconnecting; // Disconnect flag
public:
TCPClient(int priorityLevel);
virtual ~TCPClient();
CAT_INLINE bool Valid() { return _socket != SOCKET_ERROR; }
void Disconnect();
bool Connect(bool onlySupportIPv4, const NetAddr &remoteServerAddress);
public:
bool Post(u8 *data, u32 data_bytes, u32 skip_bytes = 0);
private:
bool ConnectEx(const NetAddr &remoteServerAddress);
bool Read(AsyncBuffer *buffer = 0);
bool Disco(AsyncBuffer *buffer = 0);
private:
bool OnConnectEx(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnRead(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnWrite(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnDisco(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
protected:
virtual void OnConnectToServer(ThreadPoolLocalStorage *tls) = 0;
virtual bool OnReadFromServer(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes) = 0; // false = disconnect
virtual bool OnWriteToServer(ThreadPoolLocalStorage *tls, AsyncBuffer *buffer, u32 bytes) = 0; // true = delete AsyncBuffer object
virtual void OnDisconnectFromServer() = 0;
};
/*
class TCPClientQueued
Base class for a TCP client that needs to queue up data for sending before
a connection has been established. e.g. Uplink for a proxy server.
PostQueuedToServer() : Call in OnConnectToServer() to post the queued messages.
*/
class TCPClientQueued : public TCPClient
{
private:
volatile bool _queuing;
Mutex _queueLock;
AsyncBuffer *_queueBuffer;
protected:
void PostQueuedToServer();
public:
TCPClientQueued(int priorityLevel);
virtual ~TCPClientQueued();
bool Post(u8 *data, u32 data_bytes, u32 skip_bytes = 0);
};
} // namespace cat
#endif // CAT_TCP_CLIENT_HPP

View File

@ -0,0 +1,102 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_TCP_CONNECTION_HPP
#define CAT_TCP_CONNECTION_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
namespace cat {
/*
class TCPConnection
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
DisconnectClient() : Disconnect the client
PostToClient() : Send a message to the client
ValidServerConnection() : Returns true iff the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnection : public ThreadRefObject
{
friend class TCPServer;
friend class ThreadPool;
public:
TCPConnection(int priorityLevel);
virtual ~TCPConnection();
bool ValidServerConnection();
void DisconnectClient();
bool PostToClient(void *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(u8 *data, u32 bytes) = 0; // false = disconnect
virtual void OnWriteToClient(u32 bytes) = 0;
virtual void OnDisconnectFromClient() = 0;
private:
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
TypedOverlapped *_recvOv;
volatile u32 _disconnecting;
private:
bool AcceptConnection(Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
bool QueueWSARecv();
void OnWSARecvComplete(int error, u32 bytes);
bool QueueWSASend(TypedOverlapped *sendOv, u32 bytes);
void OnWSASendComplete(int error, u32 bytes);
bool QueueDisconnectEx();
void OnDisconnectExComplete(int error);
};
} // namespace cat
#endif // CAT_TCP_CONNECTION_HPP

View File

@ -0,0 +1,98 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_TCP_CONNEXION_HPP
#define CAT_TCP_CONNEXION_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
namespace cat {
/*
class TCPConnexion
Object that represents a TCPServer's connection from a TCPClient
Object is instantiated just before accepting a connection
Disconnect() : Disconnect the client
Post() : Send a message to the client
Valid() : Returns true if the connection is valid
OnConnectFromClient() : Return false to deny this connection
OnReadFromClient() : Return false to disconnect the client in response to a message
OnWriteToClient() : Informs the derived class that data has been sent
OnDisconectFromClient() : Informs the derived class that the client has disconnected
*/
class TCPConnexion : public ThreadRefObject
{
friend class TCPServer;
Socket _socket;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
volatile u32 _disconnecting;
bool Accept(ThreadPoolLocalStorage *tls, Socket listenSocket, Socket acceptSocket,
LPFN_DISCONNECTEX lpfnDisconnectEx, const NetAddr &acceptAddress,
const NetAddr &remoteClientAddress);
public:
TCPConnexion(int priorityLevel);
virtual ~TCPConnexion();
CAT_INLINE bool Valid() { return _socket != SOCKET_ERROR; }
void Disconnect();
bool Post(u8 *data, u32 data_bytes, u32 skip_bytes = 0);
private:
bool Read(AsyncBuffer *buffer = 0);
bool Disco(AsyncBuffer *buffer = 0);
private:
bool OnRead(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnWrite(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnDisco(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
protected:
virtual bool OnConnectFromClient(ThreadPoolLocalStorage *tls, const NetAddr &remoteClientAddress) = 0; // false = disconnect
virtual bool OnReadFromClient(ThreadPoolLocalStorage *tls, u8 *data, u32 bytes) = 0; // false = disconnect
virtual bool OnWriteToClient(ThreadPoolLocalStorage *tls, AsyncBuffer *buffer, u32 bytes) = 0; // true = delete AsyncBuffer
virtual void OnDisconnectFromClient() = 0;
};
} // namespace cat
#endif // CAT_TCP_CONNEXION_HPP

View File

@ -0,0 +1,97 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_TCP_SERVER_HPP
#define CAT_TCP_SERVER_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
namespace cat {
/*
class TCPServer
Object that represents a TCP server bound to a single port
Overload InstantiateServerConnexion() to subclass connections with the server
*/
class TCPServer : public ThreadRefObject
{
friend class TCPConnexion;
Socket _socket;
LPFN_ACCEPTEX _lpfnAcceptEx;
LPFN_GETACCEPTEXSOCKADDRS _lpfnGetAcceptExSockAddrs;
LPFN_DISCONNECTEX _lpfnDisconnectEx;
Port _port;
struct AcceptTag
{
Socket acceptSocket;
// Space pre-allocated to receive addresses
// NOTE: This is not necessarily how the addresses are organized in memory
struct
{
// Not necessarily an IPv6 address
sockaddr_in6 addr[2];
u8 padding[2*16];
} addresses;
};
bool PostAccept(AsyncBuffer *buffer = 0);
bool QueueAccepts();
public:
TCPServer(int priorityLevel);
virtual ~TCPServer();
bool ValidServer();
Port GetPort();
bool Bind(bool onlySupportIPv4, Port port = 0);
void Close();
protected:
// Return true to release overlapped object memory, or return false to keep it
virtual bool OnAccept(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
protected:
virtual TCPConnexion *InstantiateServerConnexion() = 0;
};
} // namespace cat
#endif // CAT_TCP_SERVER_HPP

View File

@ -0,0 +1,105 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_UDP_ENDPOINT_HPP
#define CAT_UDP_ENDPOINT_HPP
/*
Windows version of thread pool sockets with IO Completion Ports
Included from <cat/net/ThreadPoolSockets.hpp>
Do not include directly
*/
namespace cat {
/*
class UDPEndpoint
Object that represents a UDP endpoint bound to a single port
*/
class UDPEndpoint : public ThreadRefObject
{
Socket _socket;
Port _port;
volatile u32 _closing;
bool _ipv6;
struct RecvFromTag
{
// Not necessarily an IPv6 address, but we allocate enough space for one
int addrLen;
sockaddr_in6 addr;
};
public:
UDPEndpoint(int priorityLevel);
virtual ~UDPEndpoint();
CAT_INLINE bool Valid() { return _socket != SOCKET_ERROR; }
Port GetPort();
// Is6() result is only valid AFTER Bind()
CAT_INLINE bool Is6() { return _ipv6; }
CAT_INLINE bool IsClosed() { return _closing != 0; }
// For servers: Bind() with ignoreUnreachable = true ((default))
// For clients: Bind() with ignoreUnreachable = false and call this
// after the first packet from the server is received.
bool IgnoreUnreachable();
// Disabled by default; useful for MTU discovery
bool DontFragment(bool df = true);
void Close(); // Invalidates this object
bool Bind(bool onlySupportIPv4, Port port = 0, bool ignoreUnreachable = true, int kernelReceiveBufferBytes = 0);
bool QueueWSARecvFrom();
// If Is6() == true, the address must be promoted to IPv6
// before calling PostWrite() with addr.PromoteTo6()
// skip_bytes: Number of bytes to skip at the start of the post buffer
bool Post(const NetAddr &addr, u8 *data, u32 data_bytes, u32 skip_bytes = 0);
private:
bool Read(AsyncBuffer *buffer = 0);
bool OnReadComplete(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
bool OnWriteComplete(ThreadPoolLocalStorage *tls, int error, AsyncBuffer *buffer, u32 bytes);
protected:
virtual void OnRead(ThreadPoolLocalStorage *tls, const NetAddr &addr, u8 *data, u32 bytes) = 0; // false = close
virtual bool OnWrite(ThreadPoolLocalStorage *tls, AsyncBuffer *buffer, u32 bytes) { return true; } // false = do not delete AsyncBase object
virtual void OnClose() = 0;
virtual void OnUnreachable(const NetAddr &addr) {} // Only IP is valid
};
} // namespace cat
#endif // CAT_UDP_ENDPOINT_HPP