/*CAsyncSocketEx by Tim Kosse (Tim.Kosse@gmx.de)
Version 1.2 (2003-03-28)
--------------------------------------------------------
Introduction:
-------------
CAsyncSocketEx is a replacement for the MFC class CAsyncSocket.
This class was written because CAsyncSocket is not the fastest WinSock
wrapper and it's very hard to add new functionality to CAsyncSocket
derived classes. This class offers the same functionality as CAsyncSocket.
Also, CAsyncSocketEx offers some enhancements which were not possible with
CAsyncSocket without some tricks.
How do I use it?
----------------
Basically exactly like CAsyncSocket.
To use CAsyncSocketEx, just replace all occurrences of CAsyncSocket in your
code with CAsyncSocketEx, if you did not enhance CAsyncSocket yourself in
any way, you won't have to change anything else in your code.
Why is CAsyncSocketEx faster?
-----------------------------
CAsyncSocketEx is slightly faster when dispatching notification event messages.
First have a look at the way CAsyncSocket works. For each thread that uses
CAsyncSocket, a window is created. CAsyncSocket calls WSAAsyncSelect with
the handle of that window. Until here, CAsyncSocketEx works the same way.
But CAsyncSocket uses only one window message (WM_SOCKET_NOTIFY) for all
sockets within one thread. When the window recieve WM_SOCKET_NOTIFY, wParam
contains the socket handle and the window looks up an CAsyncSocket instance
using a map. CAsyncSocketEx works differently. It's helper window uses a
wide range of different window messages (WM_USER through 0xBFFF) and passes
a different message to WSAAsyncSelect for each socket. When a message in
the specified range is received, CAsyncSocketEx looks up the pointer to a
CAsyncSocketEx instance in an Array using the index of message - WM_USER.
As you can see, CAsyncSocketEx uses the helper window in a more efficient
way, as it don't have to use the slow maps to lookup it's own instance.
Still, speed increase is not very much, but it may be noticeable when using
a lot of sockets at the same time.
Please note that the changes do not affect the raw data throughput rate,
CAsyncSocketEx only dispatches the notification messages faster.
What else does CAsyncSocketEx offer?
------------------------------------
CAsyncSocketEx offers a flexible layer system. One example is the proxy layer.
Just create an instance of the proxy layer, configure it and add it to the layer
chain of your CAsyncSocketEx instance. After that, you can connect through
proxies.
Benefit: You don't have to change much to use the layer system.
Another layer that is currently in development is the SSL layer to establish
SSL encrypted connections.
License
-------
Feel free to use this class, as long as you don't claim that you wrote it
and this copyright notice stays intact in the source files.
If you use this class in commercial applications, please send a short message
to tim.kosse@gmx.de
*/
#include "stdafx.h"
#include "AsyncSocketEx.h"
#include "wtypes.h"
#include "oleauto.h"
#include "atlconv.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#endif
#ifndef CCRITICALSECTIONWRAPPERINCLUDED
class CCriticalSectionWrapper
{
public:
CCriticalSectionWrapper()
{
InitializeCriticalSection(&m_criticalSection);
}
~CCriticalSectionWrapper()
{
DeleteCriticalSection(&m_criticalSection);
}
void Lock()
{
EnterCriticalSection(&m_criticalSection);
}
void Unlock()
{
LeaveCriticalSection(&m_criticalSection);
}
protected:
CRITICAL_SECTION m_criticalSection;
};
#define CCRITICALSECTIONWRAPPERINCLUDED
#endif
CCriticalSectionWrapper CAsyncSocketEx::m_sGlobalCriticalSection;
CAsyncSocketEx::t_AsyncSocketExThreadDataList *CAsyncSocketEx::m_spAsyncSocketExThreadDataList=0;
#ifndef _AFX
#ifndef VERIFY
#define VERIFY(x) (void(x))
#endif //VERIFY
#ifndef ASSERT
#define ASSERT(x)
#endif //ASSERT
#endif //_AFX
/////////////////////////////
//Helper Window class
#define WM_SOCKETEX_NOTIFY (WM_USER+2)
#define MAX_SOCKETS (0xBFFF-WM_SOCKETEX_NOTIFY+1)
class CAsyncSocketExHelperWindow
{
public:
CAsyncSocketExHelperWindow()
{
//Initialize data
m_pAsyncSocketExWindowData=new t_AsyncSocketExWindowData[512]; //Reserve space for 512 active sockets
memset(m_pAsyncSocketExWindowData, 0, 512*sizeof(t_AsyncSocketExWindowData));
m_nWindowDataSize=512;
m_nSocketCount=0;
m_nWindowDataPos=0;
//Create window
WNDCLASSEX wndclass;
wndclass.cbSize=sizeof wndclass;
wndclass.style=0;
wndclass.lpfnWndProc=WindowProc;
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
wndclass.hInstance=GetModuleHandle(0);
wndclass.hIcon=0;
wndclass.hCursor=0;
wndclass.hbrBackground=0;
wndclass.lpszMenuName=0;
wndclass.lpszClassName=_T("CAsyncSocketEx Helper Window");
wndclass.hIconSm=0;
RegisterClassEx(&wndclass);
m_hWnd=CreateWindow(_T("CAsyncSocketEx Helper Window"), _T("CAsyncSocketEx Helper Window"), 0, 0, 0, 0, 0, 0, 0, 0, GetModuleHandle(0));
ASSERT(m_hWnd);
SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
};
virtual ~CAsyncSocketExHelperWindow()
{
//Clean up socket storage
delete [] m_pAsyncSocketExWindowData;
m_pAsyncSocketExWindowData=0;
m_nWindowDataSize=0;
m_nSocketCount=0;
//Destroy window
if (m_hWnd)
{
DestroyWindow(m_hWnd);
m_hWnd=0;
}
}
//Adds a socket to the list of attached sockets
BOOL AddSocket(CAsyncSocketEx *pSocket, int &nSocketIndex)
{
ASSERT(pSocket);
if (!m_nWindowDataSize)
{
ASSERT(!m_nSocketCount);
m_nWindowDataSize=512;
m_pAsyncSocketExWindowData=new t_AsyncSocketExWindowData[512]; //Reserve space for 512 active sockets
memset(m_pAsyncSocketExWindowData, 0, 512*sizeof(t_AsyncSocketExWindowData));
}
if (nSocketIndex!=-1)
{
ASSERT(m_pAsyncSocketExWindowData);
ASSERT(m_nWindowDataSize>nSocketIndex);
ASSERT(m_pAsyncSocketExWindowData[nSocketIndex].m_pSocket==pSocket);
ASSERT(m_nSocketCount);
return TRUE;
}
//Increase socket storage if too small
if (m_nSocketCount>=(m_nWindowDataSize-10))
{
int nOldWindowDataSize=m_nWindowDataSize;
ASSERT(m_nWindowDataSize<MAX_SOCKETS);
m_nWindowDataSize+=512;
if (m_nWindowDataSize>MAX_SOCKETS)
m_nWindowDataSize=MAX_SOCKETS;
t_AsyncSocketExWindowData *tmp=m_pAsyncSocketExWindowData;
m_pAsyncSocketExWindowData = new t_AsyncSocketExWindowData[m_nWindowDataSize];
memcpy(m_pAsyncSocketExWindowData, tmp, nOldWindowDataSize * sizeof(t_AsyncSocketExWindowData));
memset(m_pAsyncSocketExWindowData+nOldWindowDataSize, 0, (m_nWindowDataSize-nOldWindowDataSize)*sizeof(t_AsyncSocketExWindowData));
delete [] tmp;
}
//Search for free slot
for (int i=m_nWindowDataPos;i<(m_nWindowDataSize+m_nWindowDataPos);i++)
{
if (!m_pAsyncSocketExWindowData[i%m_nWindowDataSize].m_pSocket)
{
m_pAsyncSocketExWindowData[i%m_nWindowDataSize].m_pSocket=pSocket;
nSocketIndex=i%m_nWindowDataSize;
m_nWindowDataPos=(i+1)%m_nWindowDataSize;
m_nSocketCount++;
return TRUE;
}
}
//No slot found, maybe there are too much sockets!
return FALSE;
}
//Removes a socket from the socket storage
BOOL RemoveSocket(CAsyncSocketEx *pSocket, int &nSocketIndex)
{
ASSERT(pSocket);
if (nSocketIndex==-1)
return TRUE;
ASSERT(m_pAsyncSocketExWindowData);
ASSERT(m_nWindowDataSize>0);
ASSERT(m_nSocketCount>0);
ASSERT(m_pAsyncSocketExWindowData[nSocketIndex].m_pSocket==pSocket);
m_pAsyncSocketExWindowData[nSocketIndex].m_pSocket=0;
nSocketIndex=-1;
m_nSocketCount--;
return TRUE;
}
//Processes event notifications sent by the sockets or the layers
static LRESULT CALLBACK WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
if (message>=WM_SOCKETEX_NOTIFY)
{
//Verify parameters