Click here to Skip to main content
15,892,643 members
Articles / Desktop Programming / MFC

Writing Scalable Server Applications using IOCP

Rate me:
Please Sign up or sign in to vote.
4.15/5 (29 votes)
5 Feb 2001 454K   9.1K   155  
An article about using I/O Completion Ports and Winsock to write robust and scalable Windows server applications
// Written by Oz Ben Eliezer
// o_be@hotmail.com
// September, 2000

#ifndef __BUFFER_H__		// Sentinels
#define __BUFFER_H__

#include <windows.h>
#include <stdio.h>

#include "general.h"

extern int BASE_BUFFER_SIZE;
extern int BASE_BUFFER_INC;

class CAutoBuffer
{
protected:
	char *buffer;							// Yeah, this is our buffer
	int nLength;							// Current length of data
	int nSize;								// Current size of buffer
	int nIncrement;							// By how much we should increment
											// when buffer is too small
	CRITICAL_SECTION csInUse;				// Make it thread safe

	/*	
		__forceinline is a Microsoft-specific keyword, which forces the
		compiler into inlining the code. In practice, the compiler can still
		deny in inlining, but it will produce a warning. In order to create
		a non-thread-safe version of this class, just inherit from it, and
		override the XXXInUse functions, to do nothing.
	*/

	__forceinline void EnterInUse()
	{
		EnterCriticalSection(&csInUse);
	}

	__forceinline void LeaveInUse()
	{
		LeaveCriticalSection(&csInUse);
	}

	__forceinline void InitializeInUse()
	{
		InitializeCriticalSection(&csInUse);
	}

	__forceinline void DeleteInUse()
	{
		DeleteCriticalSection(&csInUse);
	}

	/*
		The IncreaseSize(..) function re-allocates memory space for the
		buffer and copies its current contents into the new memory space.
	*/
	__forceinline void IncreaseSize(int nIncrement_)
	{
		char *newbuf;

		EnterInUse();

		newbuf = new char [nSize + nIncrement_];
		memcpy(newbuf, buffer, nSize);

		delete [] buffer;
		buffer = newbuf;
		nSize += nIncrement_;

		LeaveInUse();
	}

	__forceinline void IncreaseSize()
	{
		IncreaseSize(nIncrement);
	}

public:
	CAutoBuffer(int nIncrement_) : buffer(NULL),
		nLength(0),
		nIncrement(nIncrement_)
	{
		InitializeInUse();
		buffer = new char [BASE_BUFFER_SIZE];	// Allocate memory
		nSize = BASE_BUFFER_SIZE;
	}

	~CAutoBuffer()
	{
		DeleteInUse();
		if (buffer)
			delete [] buffer;					// De-allocate memory
	}

	/*
		I assume that memory can be allocated, and that the new keyword
		never returns NULL. I didn't embed error-detection in order to
		enhance performance. If there is not enough memory to be allocated,
		something is terribly wrong.

		Furthermore, no internal memory checks are performed inside the
		functions.
	*/

	/*
		AddToBuffer(..) adds data from a source buffer to the class' buffer.
		src: the source buffer
		len: the source buffer's size
	*/
	__forceinline void AddToBuffer(const char *src, int len)
	{
		if (len > 0)
		{
			if (len + nLength > nSize)
				IncreaseSize(BASE_BUFFER_INC > len ? BASE_BUFFER_INC : len);

			EnterInUse();

			memcpy(buffer + nLength, src, len);
			nLength += len;

			LeaveInUse();
		}
	}

	/*
		AddToBufferBeg(..) adds data from a source buffer to the beginning
		of the class' buffer.
	*/
	__forceinline void AddToBufferBeg(const char *src, int len)
	{
		if (len + nLength > nSize)
			IncreaseSize(len);

		EnterInUse();

		memmove(buffer + len, buffer, nLength);
		memcpy(buffer, src, len);
		nLength += len;

		LeaveInUse();
	}

	/*
		GetBuffer(..) copies data from the class' buffer to a destination
		buffer. dst is a pointer to the destination buffer. len is a pointer
		to an integer that specifies the size of dst. The value to which len
		points is changes by the function, to reflect the number of bytes
		transferred from the class' buffer to the destination buffer.
	*/
	__forceinline void GetBuffer(char *dst, int *len)
	{
		if (nLength > *len)
		{	// Supplied buffer is not large enough.
			GetFromBuffer(dst, len);
		}
		else
		{
			EnterInUse();

			memcpy(dst, buffer, nLength);
			*len = nLength;
			nLength = 0;		// Clear buffer;

			LeaveInUse();
		}
	}

	__forceinline void GetFromBuffer(char *dst, int *len)
	{
		if (*len > nLength)
		{
			GetBuffer(dst, len);
			return;
		}
		else if (*len != 0)
		{
			EnterInUse();

			memcpy(dst, buffer, *len);
			memmove(buffer, buffer + *len, nLength - *len);
			nLength -= *len;

			LeaveInUse();
		}
	}

	__forceinline char *GetBuffer()
	{
		return buffer;
	}

	__forceinline bool IsEmpty()
	{
		return (nLength == 0);
	}

	__forceinline bool GetPacket(tagPacket *p)
	{
		bool bRet = false;	// Assume that no packet is available.

		EnterInUse();

		if (nLength >= 4)	// Received size description
		{
			memcpy(p, buffer, 4);		// Size

			// See if enough data has arrived
			if ((unsigned)p->nLength <= (unsigned int)(nLength - 4))
			{
				// Packet is available. Copy data into packet.
				// Allocate memory for packet's buffer.
				p->buffer = new char [p->nLength];

				memcpy(p->buffer, buffer + 4, p->nLength);
				memmove(buffer, buffer + 4 + p->nLength, nLength - p->nLength - 4);
				///////////////// FIXED - memmove moves nLength - p->nLength - 4 bytes, instead of
				/////////////////			nLength - p->nLength

				nLength -= p->nLength;
				nLength -= 4;

				bRet = true;
			}
		}

		LeaveInUse();

		return bRet;
	}
};

#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions