Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / C++

A Simple and Easy to Use IOCP Server Framework

Rate me:
Please Sign up or sign in to vote.
3.43/5 (8 votes)
30 Jun 2008LGPL33 min read 44.1K   1.7K   35  
An introduction to SPServer Framework
/*
 * Copyright 2007 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <string.h>

#ifdef WIN32
#include "spgetopt.h"
#endif

#include "spporting.hpp"

#include "event.h"

static const char * gHost = "127.0.0.1";
static int gPort = 3333;
static int gMsgs = 10;
static int gClients = 10;
static int gConnWait = 0;
static int gSockWait = 0;

static time_t gStartTime = 0;

struct SP_TestClient {
	int mFd;
	struct event mReadEvent;
	struct event mWriteEvent;
	int mSendMsgs;
	int mRecvMsgs;
	char mBuffer[ 512 ];
};

void showUsage( const char * program )
{
	printf( "\nStress Test Tools for spserver example -- testecho/testchat\n\n" );
	printf( "Usage: %s [-h <host>] [-p <port>] [-c <clients>] [-m <messages>]\n"
			"\t\t\t[-w <connect wait>] [-s <socket wait>]\n\n", program );
	printf( "\t-h default is %s\n", gHost );
	printf( "\t-p default is %d\n", gPort );
	printf( "\t-c how many clients, default is %d\n", gClients );
	printf( "\t-m messages per client, default is %d\n", gMsgs );
	printf( "\t-w how many milliseconds to wait between creating every 100 connections, default is %d\n", gConnWait );
	printf( "\t-s how many milliseconds to wait between every event loop, default is %d\n", gSockWait );
	printf( "\n" );
}

void close_read( SP_TestClient * client )
{
	//fprintf( stderr, "#%d close read\n", client->mFd );
	event_del( &client->mReadEvent );
	gClients--;
}

void close_write( SP_TestClient * client )
{
	//fprintf( stderr, "#%d close write\n", client->mFd );
	event_del( &client->mWriteEvent );
}

void close_client( SP_TestClient * client )
{
	close_write( client );
	close_read( client );
}

void on_read( int fd, short events, void *arg )
{
	SP_TestClient * client = ( SP_TestClient * ) arg;

	if( EV_READ & events ) {
		int len = recv( fd, client->mBuffer, sizeof( client->mBuffer ), 0 );
		if( len <= 0 ) {
			if( len < 0 && EAGAIN != errno ) {
				fprintf( stderr, "#%d on_read error, count %d, errno %d, %s\n",
						fd, client->mRecvMsgs, errno, strerror( errno ) );
			}
			close_client( client );
		} else {
			for( int i = 0; i < len; i++ ) {
				//if( 10 == fd ) printf( "%c", client->mBuffer[i] );
				if( '\n' == client->mBuffer[i] ) client->mRecvMsgs++;
			}
		}
	} else {
		fprintf( stderr, "#%d on_read timeout\n", fd );
		close_client( client );
	}
}

void on_write( int fd, short events, void *arg )
{
	SP_TestClient * client = ( SP_TestClient * ) arg;

	if( EV_WRITE & events ) {
		client->mSendMsgs++;

		if( client->mSendMsgs >= gMsgs ) {
			snprintf( client->mBuffer, sizeof( client->mBuffer ), "quit\n" );
		} else {
			snprintf( client->mBuffer, sizeof( client->mBuffer ),
				"mail #%d, It's good to see how people hire; "
				"that tells us how to market ourselves to them.\n", client->mSendMsgs );
		}

		int len = send( fd, client->mBuffer, strlen( client->mBuffer ), 0 );

		if( len <= 0 && EAGAIN != errno ) {
			fprintf( stderr, "#%d on_write error, errno %d, %s\n", fd, errno, strerror( errno ) );
			close_client( client );
		} else {
			if( client->mSendMsgs >= gMsgs ) close_write( client );
		}
	} else {
		fprintf( stderr, "#%d on_write timeout\n", fd );
		close_client( client );
	}
}

void parse_arg( int argc, char * argv[] )
{
	extern char *optarg ;
	int c ;

	while( ( c = getopt ( argc, argv, "h:p:c:m:w:s:v" )) != EOF ) {
		switch ( c ) {
			case 'h' :
				gHost = optarg;
				break;
			case 'p':
				gPort = atoi( optarg );
				break;
			case 'c' :
				gClients = atoi ( optarg );
				break;
			case 'm' :
				gMsgs = atoi( optarg );
				break;
			case 'w':
				gConnWait = atoi( optarg );
				break;
			case 's':
				gSockWait = atoi( optarg );
				break;
			case 'v' :
			case '?' :
				showUsage( argv[0] );
				exit( 0 );
		}
	}
}

int main( int argc, char * argv[] )
{
	parse_arg( argc, argv );

#ifdef SIGPIPE
	signal( SIGPIPE, SIG_IGN );
#endif

	sp_initsock();

	event_init();

	SP_TestClient * clientList = (SP_TestClient*)calloc( gClients, sizeof( SP_TestClient ) );

	struct sockaddr_in sin;
	memset( &sin, 0, sizeof(sin) );
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr( gHost );
	sin.sin_port = htons( gPort );

	int totalClients = gClients, i = 0;

	printf( "Create %d connections to server, it will take some minutes to complete.\n", gClients );
	for( i = 0; i < gClients; i++ ) {
		SP_TestClient * client = clientList + i;

		client->mFd = socket( AF_INET, SOCK_STREAM, 0 );
		if( client->mFd < 0 ) {
			fprintf(stderr, "#%d, socket failed, errno %d, %s\n", i, errno, strerror( errno ) );
#ifdef WIN32
			spwin32_pause_console();
#endif
			return -1;
		}

		if( connect( client->mFd, (struct sockaddr *)&sin, sizeof(sin) ) != 0) {
			fprintf(stderr, "#%d, connect failed, errno %d, %s\n", i, errno, strerror( errno ) );
#ifdef WIN32
			spwin32_pause_console();
#endif
			return -1;
		}

		event_set( &client->mWriteEvent, client->mFd, EV_WRITE | EV_PERSIST, on_write, client );
		event_add( &client->mWriteEvent, NULL );

		event_set( &client->mReadEvent, client->mFd, EV_READ | EV_PERSIST, on_read, client );
		event_add( &client->mReadEvent, NULL );

		if( 0 == ( i % 10 ) ) write( fileno( stdout ), ".", 1 );

		if( gConnWait > 0 && ( i > 0 ) && ( 0 == ( i % 100 ) ) ) usleep( gConnWait * 1000 );
	}

	time( &gStartTime );

	struct timeval startTime, stopTime;

	sp_gettimeofday( &startTime, NULL );

	time_t lastInfoTime = time( NULL );

	// start event loop until all clients are exit
	while( gClients > 0 ) {
		event_loop( EVLOOP_ONCE );

		if( time( NULL ) - lastInfoTime > 5 ) {
			time( &lastInfoTime );
			printf( "waiting for %d client(s) to exit\n", gClients );
		}

		if( gSockWait > 0 ) usleep( gSockWait * 1000 );
	}

	sp_gettimeofday( &stopTime, NULL );

	double totalTime = (double) ( 1000000 * ( stopTime.tv_sec - startTime.tv_sec )
			+ ( stopTime.tv_usec - startTime.tv_usec ) ) / 1000000;

	// show result
	printf( "\n\nTest result :\n" );
	printf( "Host %s, Port %d, ConnWait: %d, SockWait: %d\n", gHost, gPort, gConnWait, gSockWait );
	printf( "Clients : %d, Messages Per Client : %d\n", totalClients, gMsgs );
	printf( "ExecTimes: %.6f seconds\n\n", totalTime );

	printf( "client\tSend\tRecv\n" );
	int totalSend = 0, totalRecv = 0;
	for( i = 0; i < totalClients; i++ ) {
		SP_TestClient * client = clientList + i;

		//printf( "client#%d : %d\t%d\n", i, client->mSendMsgs, client->mRecvMsgs );

		totalSend += client->mSendMsgs;
		totalRecv += client->mRecvMsgs;

		sp_close( client->mFd );
	}

	printf( "total   : %d\t%d\n", totalSend, totalRecv );
	printf( "average : %.0f/s\t%.0f/s\n", totalSend / totalTime, totalRecv / totalTime );

	free( clientList );

#ifdef WIN32
	spwin32_pause_console();
#endif

	return 0;
}

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.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


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

Comments and Discussions