| Tenouk | Home | Previous | Next | Download  |  Site Index  |  Disclaimer  |  Privacy | Contact | Tell friends |


 

 

C++, MFC, Winsock and WinInet Part 6

 

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2 and some Figure screen snapshots have been taken on Windows 2000 server. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer.  Supplementary item is WEBSITE.

 

 

 

Index:
 
The Coding Part...Continue

 

 

Next, add/modify the implementation part of the previous member functions and variables in Blocksock.cpp.

 

// Blocksock.cpp: implementation of the

// CBlockingSocketException, CBlockingSocket, CHttpBlockingSocket

//////////////////////////////////////////////////////////////////////

 

#include "stdafx.h"

#include "myex33a.h"

#include "Blocksock.h"

 

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

 

// Class CBlockingSocketException

IMPLEMENT_DYNAMIC(CBlockingSocketException, CException)

 

CBlockingSocketException::CBlockingSocketException(char* pchMessage)

{

       m_strMessage = pchMessage;

       m_nError = WSAGetLastError();

}

 

BOOL CBlockingSocketException::GetErrorMessage(LPTSTR lpstrError, UINT nMaxError,

              PUINT pnHelpContext /*= NULL*/)

{

 

       char text[200];

       if(m_nError == 0)

       { wsprintf(text, "%s error", (const char*) m_strMessage); }

       else

       { wsprintf(text, "%s error #%d", (const char*) m_strMessage, m_nError); }

       strncpy(lpstrError, text, nMaxError - 1);

       return TRUE;

}

 

// Class CBlockingSocket

IMPLEMENT_DYNAMIC(CBlockingSocket, CObject)

 

void CBlockingSocket::Cleanup()

{

       // doesn't throw an exception because it's called in a catch block

       if(m_hSocket == NULL) return;

       VERIFY(closesocket(m_hSocket) != SOCKET_ERROR);

       m_hSocket = NULL;

}

 

void CBlockingSocket::Create(int nType /* = SOCK_STREAM */)

{

       ASSERT(m_hSocket == NULL);

       if((m_hSocket = socket(AF_INET, nType, 0)) == INVALID_SOCKET) {

              throw new CBlockingSocketException("Create");

       }

}

 

void CBlockingSocket::Bind(LPCSOCKADDR psa)

{

       ASSERT(m_hSocket != NULL);

       if(bind(m_hSocket, psa, sizeof(SOCKADDR)) == SOCKET_ERROR) {

              throw new CBlockingSocketException("Bind");

       }

}

 

void CBlockingSocket::Listen()

{

       ASSERT(m_hSocket != NULL);

       if(listen(m_hSocket, 5) == SOCKET_ERROR) {

              throw new CBlockingSocketException("Listen");

       }

}

 

BOOL CBlockingSocket::Accept(CBlockingSocket& sConnect, LPSOCKADDR psa)

{

       ASSERT(m_hSocket != NULL);

       ASSERT(sConnect.m_hSocket == NULL);

       int nLengthAddr = sizeof(SOCKADDR);

       sConnect.m_hSocket = accept(m_hSocket, psa, &nLengthAddr);

       if(sConnect == INVALID_SOCKET) {

              // no exception if the listen was canceled

              if(WSAGetLastError() != WSAEINTR) {

                     throw new CBlockingSocketException("Accept");

              }

              return FALSE;

       }

       return TRUE;

}

 

void CBlockingSocket::Close()

{

       if (NULL == m_hSocket)

              return;

 

       if(closesocket(m_hSocket) == SOCKET_ERROR) {

              // should be OK to close if closed already

              throw new CBlockingSocketException("Close");

       }

       m_hSocket = NULL;

}

 

void CBlockingSocket::Connect(LPCSOCKADDR psa)

{

       ASSERT(m_hSocket != NULL);

       // should timeout by itself

       if(connect(m_hSocket, psa, sizeof(SOCKADDR)) == SOCKET_ERROR) {

              throw new CBlockingSocketException("Connect");

       }

}

 

int CBlockingSocket::Write(const char* pch, const int nSize, const int nSecs)

{

       int nBytesSent = 0;

       int nBytesThisTime;

       const char* pch1 = pch;

       do {

              nBytesThisTime = Send(pch1, nSize - nBytesSent, nSecs);

              nBytesSent += nBytesThisTime;

              pch1 += nBytesThisTime;

       } while(nBytesSent < nSize);

       return nBytesSent;

}

 

int CBlockingSocket::Send(const char* pch, const int nSize, const int nSecs)

{

       ASSERT(m_hSocket != NULL);

       // returned value will be less than nSize if client cancels the reading

       FD_SET fd = {1, m_hSocket};

       TIMEVAL tv = {nSecs, 0};

       if(select(0, NULL, &fd, NULL, &tv) == 0) {

              throw new CBlockingSocketException("Send timeout");

       }

       int nBytesSent;

       if((nBytesSent = send(m_hSocket, pch, nSize, 0)) == SOCKET_ERROR) {

              throw new CBlockingSocketException("Send");

       }

       return nBytesSent;

}

 

int CBlockingSocket::Receive(char* pch, const int nSize, const int nSecs)

{

       ASSERT(m_hSocket != NULL);

       FD_SET fd = {1, m_hSocket};

       TIMEVAL tv = {nSecs, 0};

       if(select(0, &fd, NULL, NULL, &tv) == 0) {

              throw new CBlockingSocketException("Receive timeout");

       }

 

       int nBytesReceived;

       if((nBytesReceived = recv(m_hSocket, pch, nSize, 0)) == SOCKET_ERROR) {

              throw new CBlockingSocketException("Receive");

       }

       return nBytesReceived;

}

 

int CBlockingSocket::ReceiveDatagram(char* pch, const int nSize, LPSOCKADDR psa, const int nSecs)

{

       ASSERT(m_hSocket != NULL);

       FD_SET fd = {1, m_hSocket};

       TIMEVAL tv = {nSecs, 0};

       if(select(0, &fd, NULL, NULL, &tv) == 0) {

              throw new CBlockingSocketException("Receive timeout");

       }

 

       // input buffer should be big enough for the entire datagram

       int nFromSize = sizeof(SOCKADDR);

       int nBytesReceived = recvfrom(m_hSocket, pch, nSize, 0, psa, &nFromSize);

       if(nBytesReceived == SOCKET_ERROR) {

              throw new CBlockingSocketException("ReceiveDatagram");

       }

       return nBytesReceived;

}

 

int CBlockingSocket::SendDatagram(const char* pch, const int nSize, LPCSOCKADDR psa, const int nSecs)

{

       ASSERT(m_hSocket != NULL);

       FD_SET fd = {1, m_hSocket};

       TIMEVAL tv = {nSecs, 0};

       if(select(0, NULL, &fd, NULL, &tv) == 0) {

              throw new CBlockingSocketException("Send timeout");

       }

 

       int nBytesSent = sendto(m_hSocket, pch, nSize, 0, psa, sizeof(SOCKADDR));

       if(nBytesSent == SOCKET_ERROR) {

              throw new CBlockingSocketException("SendDatagram");

       }

       return nBytesSent;

}

 

void CBlockingSocket::GetPeerAddr(LPSOCKADDR psa)

{

       ASSERT(m_hSocket != NULL);

       // gets the address of the socket at the other end

       int nLengthAddr = sizeof(SOCKADDR);

       if(getpeername(m_hSocket, psa, &nLengthAddr) == SOCKET_ERROR) {

              throw new CBlockingSocketException("GetPeerName");

       }

}

 

void CBlockingSocket::GetSockAddr(LPSOCKADDR psa)

{

       ASSERT(m_hSocket != NULL);

       // gets the address of the socket at this end

       int nLengthAddr = sizeof(SOCKADDR);

       if(getsockname(m_hSocket, psa, &nLengthAddr) == SOCKET_ERROR) {

              throw new CBlockingSocketException("GetSockName");

       }

}

 

//static

CSockAddr CBlockingSocket::GetHostByName(const char* pchName, const USHORT ushPort /* = 0 */)

{

       hostent* pHostEnt = gethostbyname(pchName);

       if(pHostEnt == NULL) {

              throw new CBlockingSocketException("GetHostByName");

       }

       ULONG* pulAddr = (ULONG*) pHostEnt->h_addr_list[0];

       SOCKADDR_IN sockTemp;

       sockTemp.sin_family = AF_INET;

       sockTemp.sin_port = htons(ushPort);

       sockTemp.sin_addr.s_addr = *pulAddr; // address is already in network byte order

       return sockTemp;

}

 

//static

const char* CBlockingSocket::GetHostByAddr(LPCSOCKADDR psa)

{

       hostent* pHostEnt = gethostbyaddr((char*) &((LPSOCKADDR_IN) psa)->sin_addr.s_addr, 4, PF_INET);

       if(pHostEnt == NULL) {

              throw new CBlockingSocketException("GetHostByAddr");

       }

       return pHostEnt->h_name; // caller shouldn't delete this memory

}

 

// Class CHttpBlockingSocket

IMPLEMENT_DYNAMIC(CHttpBlockingSocket, CBlockingSocket)

 

CHttpBlockingSocket::CHttpBlockingSocket()

{

       m_pReadBuf = new char[nSizeRecv];

       m_nReadBuf = 0;

}

 

CHttpBlockingSocket::~CHttpBlockingSocket()

{

       delete [] m_pReadBuf;

}

 

int CHttpBlockingSocket::ReadHttpHeaderLine(char* pch, const int nSize, const int nSecs)

// reads an entire header line through CRLF (or socket close)

// inserts zero string terminator, object maintains a buffer

{

       int nBytesThisTime = m_nReadBuf;

       int nLineLength = 0;

       char* pch1 = m_pReadBuf;

       char* pch2;

       do {

       // look for lf (assume preceded by cr)

       if((pch2 = (char*) memchr(pch1 , '\n', nBytesThisTime)) != NULL) {

              ASSERT((pch2) > m_pReadBuf);

              ASSERT(*(pch2 - 1) == '\r');

              nLineLength = (pch2 - m_pReadBuf) + 1;

              if(nLineLength >= nSize) nLineLength = nSize - 1;

              memcpy(pch, m_pReadBuf, nLineLength); // copy the line to caller

              m_nReadBuf -= nLineLength;

             // shift remaining characters left

              memmove(m_pReadBuf, pch2 + 1, m_nReadBuf);

                     break;

              }

              pch1 += nBytesThisTime;

              nBytesThisTime = Receive(m_pReadBuf + m_nReadBuf, nSizeRecv - m_nReadBuf, nSecs);

              if(nBytesThisTime <= 0)

              { // sender closed socket or line longer than buffer

                     throw new CBlockingSocketException("ReadHeaderLine");

              }

              m_nReadBuf += nBytesThisTime;

       }

       while(TRUE);

       *(pch + nLineLength) = '\0';

       return nLineLength;

}

 

int CHttpBlockingSocket::ReadHttpResponse(char* pch, const int nSize, const int nSecs)

// reads remainder of a transmission through buffer full or socket close

// (assume headers have been read already)

{

       int nBytesToRead, nBytesThisTime, nBytesRead = 0;

       if(m_nReadBuf > 0) { // copy anything already in the recv buffer

              memcpy(pch, m_pReadBuf, m_nReadBuf);

              pch += m_nReadBuf;

              nBytesRead = m_nReadBuf;

              m_nReadBuf = 0;

       }

       do { // now pass the rest of the data directly to the caller

              nBytesToRead = min(nSizeRecv, nSize - nBytesRead);

              nBytesThisTime = Receive(pch, nBytesToRead, nSecs);

              if(nBytesThisTime <= 0) break; // sender closed the socket

              pch += nBytesThisTime;

              nBytesRead += nBytesThisTime;

       }

       while(nBytesRead <= nSize);

       return nBytesRead;

}

 

void LogBlockingSocketException(LPVOID pParam, char* pch, CBlockingSocketException* pe)

{      // pParam holds the HWND for the destination window (in another thread)

       CString strGmt = CTime::GetCurrentTime().FormatGmt("%m/%d/%y %H:%M:%S GMT");

       char text1[200], text2[50];

       pe->GetErrorMessage(text2, 49);

       wsprintf(text1, "WINSOCK ERROR--%s %s -- %s\r\n", pch, text2, (const char*) strGmt);

       ::SendMessage((HWND) pParam, EM_SETSEL, (WPARAM) 65534, 65535);

       ::SendMessage((HWND) pParam, EM_REPLACESEL, (WPARAM) 0, (LPARAM) text1);

}

 

 

Listing 17.

 

Before we forget, add the precompiled header for the Winsock2.h, else there will be errors during the linking. Select Project Settings menu. In the Link page on the right window, add Ws2_32.lib as shown below. Here, we are using Winsock 2 library. It works fine.

 

Winsock, C++ and MFC - Figure 74: Adding library module to Visual C++ project.

 

Figure 74: Adding library module to Visual C++ project.

 

Then modify/add the Utility.h and Utility.cpp code as shown below.

 

// Utility.h: interface for the CCallbackInternetSession class.

//

//////////////////////////////////////////////////////////////////////

 

#if !defined(AFX_UTILITY_H__C57D5E4E_7F33_4249_BEA4_E69BD55D710B__INCLUDED_)

#define AFX_UTILITY_H__C57D5E4E_7F33_4249_BEA4_E69BD55D710B__INCLUDED_

 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

 

#define WM_CALLBACK WM_USER + 5

 

extern volatile int g_nConnection;

extern CString g_strServerName; // used by both winsock and wininet code

extern CString g_strServerIP; // used by both winsock and wininet code

extern CString g_strFile;

extern char g_pchStatus[];

extern HWND g_hMainWnd;

extern CCriticalSection g_csStatus;

extern CString g_strIPClient;

extern volatile UINT g_nPort;

extern CString g_strProxy;

extern BOOL g_bUseProxy;

extern volatile BOOL g_bListening;

extern CString g_strDirect;

extern CString g_strIPServer;

extern volatile UINT g_nPortServer;

extern CString g_strURL;

extern CString g_strDefault;

 

extern UINT ClientUrlThreadProc(LPVOID pParam);

extern UINT ServerThreadProc(LPVOID pParam);

extern UINT ClientWinInetThreadProc(LPVOID pParam);

extern UINT ClientSocketThreadProc(LPVOID pParam);

 

extern void LogInternetException(LPVOID pParam, CInternetException* pe);

 

class CCallbackInternetSession : public CInternetSession

{

public:

       CCallbackInternetSession( LPCTSTR pstrAgent = NULL, DWORD dwContext = 1,

       DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS, LPCTSTR pstrProxyName = NULL,

       LPCTSTR pstrProxyBypass = NULL, DWORD dwFlags = 0 );

protected:

       virtual void OnStatusCallback(DWORD dwContext, DWORD dwInternalStatus,

       LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);

};

 

#endif // !defined(AFX_UTILITY_H__C57D5E4E_7F33_4249_BEA4_E69BD55D710B__INCLUDED_)

 

 

Listing 18.

 

// Utility.cpp: implementation of the CCallbackInternetSession class

// and other functions - contains stuff used by more than one thread

//

//////////////////////////////////////////////////////////////////////

 

#include "stdafx.h"

#include "myex33a.h"

#include "Utility.h"

#include "Blocksock.h"

 

#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif

 

// connection number

volatile int g_nConnection = 0;

// used by both winsock and wininet

CString g_strServerName = "localhost";

CString g_strServerIP;

volatile UINT g_nPort = 80;

CString g_strFile = "/custom";

 

CCallbackInternetSession::CCallbackInternetSession( LPCTSTR pstrAgent, DWORD dwContext,

        DWORD dwAccessType, LPCTSTR pstrProxyName, LPCTSTR pstrProxyBypass, DWORD dwFlags) :

       CInternetSession(pstrAgent, dwContext, dwAccessType, pstrProxyName, pstrProxyBypass, dwFlags)

{

       EnableStatusCallback();

}

 

void CCallbackInternetSession::OnStatusCallback(DWORD dwContext, DWORD dwInternalStatus,

              LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)

{

       AFX_MANAGE_STATE(AfxGetStaticModuleState());

       int errors[] = {10, 11, 20, 21, 30, 31, 40, 41, 42, 43, 50, 51, 60, 70, 100, 110, 0};

       char* text[] = {

              "Resolving name",         

              "Name resolved",      

              "Connecting to server",

              "Connected to server", 

              "Sending request",     

              "Request sent",        

              "Receiving response",  

              "Response received",   

              "Ctl response received",

              "Prefetch",            

              "Closing connection",  

              "Connection closed",   

              "Handle created",      

              "Handle closing",      

              "Request complete",    

   &n