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.
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.

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 |