Click here to Skip to main content
15,896,269 members
Articles / High Performance Computing / Parallel Processing

Use of free SocketPro package for creating super client and server applications

Rate me:
Please Sign up or sign in to vote.
4.48/5 (19 votes)
23 Feb 200211 min read 181.2K   6.3K   59  
A set of socket libraries for writing distributed computing applications over the internet
// SpeedTestDlg.cpp : implementation file
//

#include "stdafx.h"
#include "SpeedTest.h"
#include "SpeedTestDlg.h"

#include <SockUtil.h>
#include <atlbase.h>
#include "..\..\Svr\HostDnsSvs.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpeedTestDlg dialog

CSpeedTestDlg::CSpeedTestDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSpeedTestDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSpeedTestDlg)
	m_strClients = _T("");
	m_strHost = _T("");
	m_nPort = 1739;
	m_dwTime = 0;
	m_nBytes = 0;
	m_nNagleTime = 0;
	m_bNagle = FALSE;
	m_nRepeat = 100;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CSpeedTestDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSpeedTestDlg)
	DDX_Text(pDX, IDC_HOST_EDIT, m_strHost);
	DDX_Text(pDX, IDC_PORT_EDIT, m_nPort);
	DDX_Text(pDX, IDC_TIMECOST_EDIT, m_dwTime);
	DDX_Text(pDX, IDC_BYTES_EDIT, m_nBytes);
	DDX_Text(pDX, IDC_NAGLETIME_EDIT, m_nNagleTime);
	DDX_Check(pDX, IDC_NAGLE_CHECK, m_bNagle);
	DDX_Text(pDX, IDC_REPEAT_EDIT, m_nRepeat);
	DDV_MinMaxUInt(pDX, m_nRepeat, 1, 8000);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CSpeedTestDlg, CDialog)
	//{{AFX_MSG_MAP(CSpeedTestDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_CONNECT_BUTTON, OnConnectButton)
	ON_BN_CLICKED(IDC_DISCONNECT_BUTTON, OnDisconnectButton)
	ON_BN_CLICKED(IDC_ASYN_NAGLE_BUTTON, OnAsynNagleButton)
	ON_BN_CLICKED(IDC_ASYN_BATCH_BUTTON, OnAsynBatchButton)
	ON_BN_CLICKED(IDC_SYN_ONE_BUTTON, OnSynOneButton)
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_NAGLE_CHECK, OnNagleCheck)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpeedTestDlg message handlers

BOOL CSpeedTestDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSpeedTestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSpeedTestDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSpeedTestDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

bool CSpeedTestDlg::Run()
{
	DWORD	dwBytes=0;

	//make sure if any data is available
	if(!IOCtl(FIONREAD, dwBytes)||dwBytes<sizeof(CStreamHeader))
		return false;
	
	BYTE	*pBuffer=new BYTE[dwBytes];
	m_nBytes +=RecvBigChunk(pBuffer, dwBytes);
	m_dwTime=::GetTickCount()-m_dwStart;
	UpdateData(FALSE);

	//you could decode the binary data here
	//the binary data contains various infos such as method ids, returned data, or error codes if available here

	delete []pBuffer;
	
	//Peek if any data is available
	dwBytes=0;
	IOCtl(FIONREAD, dwBytes);
	if(dwBytes && dwBytes >= sizeof(CStreamHeader))
	{
		while(!::PostMessage(CAsySocketClient::GetWnd(), WM_SOCKET_CLIENT_NOTIFY, m_hSocket, FD_READ))
		{
			::Sleep(1);
		}
	}
	return true;
}

void CSpeedTestDlg::OnConnectButton() 
{
	UpdateData(TRUE);

	//set application instance
	SetInstance(AfxGetApp()->m_hInstance);
	
	//create a local socket
	CAsySocket::Create(0);
	
	//connect the local socket to a server (m_strHost) listening socket (m_nPort) asynchronously
	//connection time out is 30 seconds by default. 
	//If you don't like this default, call the function CAsySocketClient::SetConnectTimeout
	Connect(m_strHost, m_nPort);	
}

void CSpeedTestDlg::OnClose(WPARAM hSocket,  LPARAM lError)
{
	GetDlgItem(IDC_ASYN_NAGLE_BUTTON)->EnableWindow(FALSE);
	GetDlgItem(IDC_ASYN_BATCH_BUTTON)->EnableWindow(FALSE);
	GetDlgItem(IDC_SYN_ONE_BUTTON)->EnableWindow(FALSE);
	
	m_bNagle=FALSE;

	UpdateData(FALSE);

	CAsySocketClient::OnClose(hSocket, lError);
}

void CSpeedTestDlg::OnConnect(WPARAM hSocket,  LPARAM lError)
{
	if(lError==S_OK)
	{
		DWORD	dwOldEvents=m_lEvent;
		
		GetDlgItem(IDC_ASYN_NAGLE_BUTTON)->EnableWindow(TRUE);
		GetDlgItem(IDC_ASYN_BATCH_BUTTON)->EnableWindow(TRUE);
		GetDlgItem(IDC_SYN_ONE_BUTTON)->EnableWindow(TRUE);
		
		CStreamHeader	StreamHeader;

		CPacking		Packing;

		StreamHeader.m_nValue=HOST_DNS_SVS_SOCKET;	//HostDNS class ID

		//switch to blocking mode, and make logical in coding simpler 
		AsyncSelect(0);

		//Remember that the SocketPro server can provide multiple svervices 
		//Tells the SocketPro server to switch to HostDNS service only
		SendBigChunk(&StreamHeader, sizeof(StreamHeader));
		
		//Get the returned result
		RecvBigChunk(&StreamHeader, sizeof(StreamHeader));
		if(StreamHeader.m_nValue!=S_OK)
		{
			MessageBox("No requried class recognized!");
		}

		//switch back to non-blocking mode, and don't need the event FD_CONNECT any more
		AsyncSelect(dwOldEvents &~(FD_CONNECT));

		m_bNagle=TRUE;

		UpdateData(FALSE);
	}
	else
	{
		CString strErrMsg;
		strErrMsg.Format("Error happens in connecting to a host, and error code =%d\n", lError);
		MessageBox(strErrMsg);
	}
}

void CSpeedTestDlg::OnDisconnectButton() 
{
	GetDlgItem(IDC_ASYN_NAGLE_BUTTON)->EnableWindow(FALSE);
	GetDlgItem(IDC_ASYN_BATCH_BUTTON)->EnableWindow(FALSE);
	GetDlgItem(IDC_SYN_ONE_BUTTON)->EnableWindow(FALSE);
	
	//gracefully shut down socket
	ShutDown();
//	Close();

	m_bNagle=FALSE;

	UpdateData(FALSE);
}

void CSpeedTestDlg::OnAsynNagleButton() 
{
	UpdateData(TRUE);
	m_dwStart=::GetTickCount();
	unsigned long	nIndex=0;
	m_nBytes=0;
	CStreamHeader	StreamHeader;
	StreamHeader.m_nLen=0;
	StreamHeader.m_nValue=HOST_DNS_SVS_METHOD_ID_GET_CLIENTS;
	
	//Coalescing happens automatically through Nagle algorithm
	//Multiple StreamHeaders are sent to a SocketPro server using one big stream.
	//You can watch the sizes of big streams at the server side.
	//This will reduce data round-trips for efficient use of network.
	for(nIndex=0; nIndex<m_nRepeat; nIndex++)
	{
		//Nagle algorithm may cost a little time, which depends on your machine
		SendBigChunk(&StreamHeader, sizeof(StreamHeader));
	}
	
	m_nNagleTime=::GetTickCount()-m_dwStart;
	UpdateData(FALSE);
}

void CSpeedTestDlg::OnAsynBatchButton() 
{
	UpdateData(TRUE);
	m_dwStart=::GetTickCount();
	m_nBytes=0;
	unsigned long	nIndex=0;
	CStreamHeader	*pStreamHeader=new CStreamHeader [m_nRepeat];
	for(nIndex=0; nIndex<m_nRepeat; nIndex++)
	{
		pStreamHeader[nIndex].m_nLen=0;
		pStreamHeader[nIndex].m_nValue=HOST_DNS_SVS_METHOD_ID_GET_CLIENTS;
	}

	//Coalescing by our code instead of Nagle algorithm
	//Multiple StreamHeaders are sent to a SocketPro server using one big stream.
	//You can watch the sizes of big streams at the server side.
	//This will reduce data round-trips for efficient use of network.
	SendBigChunk(pStreamHeader, m_nRepeat*sizeof(CStreamHeader));
	delete []pStreamHeader;
	m_nNagleTime=::GetTickCount()-m_dwStart;
	UpdateData(FALSE);
}

void CSpeedTestDlg::OnSynOneButton() 
{
	char	*strBuffer;
	unsigned long	nIndex;
	CStreamHeader	StreamHeader;

	//remember the old events
	DWORD	dwOldEvents=m_lEvent;
	UpdateData(TRUE);

	m_nBytes=0;

	//switch to blocking mode
	AsyncSelect(0);

	m_dwStart=::GetTickCount();

	for(nIndex=0; nIndex<m_nRepeat; nIndex++)
	{
		StreamHeader.m_nLen=0;
		StreamHeader.m_nValue=HOST_DNS_SVS_METHOD_ID_GET_CLIENTS;

		SendBigChunk(&StreamHeader, sizeof(StreamHeader));

		//Get the returned stream header 
		m_nBytes += RecvBigChunk(&StreamHeader, sizeof(StreamHeader));
		
		strBuffer = new char [StreamHeader.m_nLen];
		
		//Retrieve the binary data
		m_nBytes += RecvBigChunk(strBuffer, StreamHeader.m_nLen);
		
		delete []strBuffer;
	}

	m_dwTime=::GetTickCount()-m_dwStart;
	UpdateData(FALSE);
	
	//switch back to non-blocking mode
	AsyncSelect(dwOldEvents);
}

void CSpeedTestDlg::OnDestroy() 
{
	if(IsOpen())
	{
		OnDisconnectButton();
	}
	CDialog::OnDestroy();	
}

void CSpeedTestDlg::OnNagleCheck() 
{
	UpdateData(TRUE);
	m_dwStart=::GetTickCount();
	if(IsOpen())
	{
		CStreamHeader	StreamHeader;
		
		//Client side
/*		if(m_bNagle)
		{
			BOOL bNoDelay=(!m_bNagle);
			SetSockOpt(TCP_NODELAY, &bNoDelay, sizeof(bNoDelay), IPPROTO_TCP);
		}
		else
		{
			BOOL bNoDelay=(!m_bNagle);
			SetSockOpt(TCP_NODELAY, &bNoDelay, sizeof(bNoDelay), IPPROTO_TCP);
		}*/
		
		//Server side
		if(m_bNagle)
			StreamHeader.m_nValue=ENABLE_NAGLE;	//enable
		else
			StreamHeader.m_nValue=DISABLE_NAGLE;	//disable	

		SendBigChunk(&StreamHeader, sizeof(StreamHeader));
	}
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
Yuancai (Charlie) Ye, an experienced C/C++ software engineer, lives in Atlanta, Georgia. He is an expert at continuous inline request/result batching, real-time stream processing, asynchronous data transferring and parallel computation for the best communication throughput and latency. He has been working at SocketPro (https://github.com/udaparts/socketpro) for more than fifteen years.

Comments and Discussions