| Tenouk C & C++ | MFC Home | C++, MFC, Winsock & WinInet 5 | C++, MFC, Winsock & WinInet 7 | Download | Site Index |


 

 

 

 

 

 

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.

  1. 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",

              "Redirect",

              "Unknown" };

       int n;

/*  // demonstrates request cancellation

       if(dwInternalStatus == INTERNET_STATUS_REQUEST_SENT) {

              AfxThrowInternetException(dwContext, 999);

       }

*/

       for(n = 0; errors[n] != 0; n++) {

              if(errors[n] == (int) dwInternalStatus) break;

       }

       g_csStatus.Lock();

         strcpy(g_pchStatus, text[n]);

         if(dwInternalStatus  == INTERNET_STATUS_RESOLVING_NAME ||

                           dwInternalStatus == INTERNET_STATUS_NAME_RESOLVED) {

              strcat(g_pchStatus, "-");

              strcat(g_pchStatus, (char*) lpvStatusInformation);

         }

         TRACE("WININET STATUS: %s\n", g_pchStatus);

       g_csStatus.Unlock();

       // frame doesn't need a handler -- message triggers OnIdle, which updates status bar

       ::PostMessage(g_hMainWnd, WM_CALLBACK, 0, 0);

}

 

void LogInternetException(LPVOID pParam, CInternetException* pe)

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

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

       char text1[300], text2[100];

       wsprintf(text1, "CLIENT ERROR: WinInet error #%d -- %s\r\n   ", pe->m_dwError, (const char*) strGmt);

       pe->GetErrorMessage(text2, 99);

       strcat(text1, text2);

       if(pe->m_dwError == 12152) {

              strcat(text1, "  URL not found?\r\n");

       }

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

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

}

 

 

Listing 19.

Add the server thread source file, ServerThread.cpp as shown below.

 

Winsock, C++ and MFC - Figure 75: Adding new file to project.

 

Figure 75: Adding new file to project.

 

Winsock, C++ and MFC - Figure 76: Adding source file to project.

 

Figure 76: Adding source file to project.

 

Copy and paste the following code.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

// serverthread.cpp

 

#include <stdafx.h>

#include "Blocksock.h"

#include "Utility.h"

#define SERVERMAXBUF 10000

#define MAXLINELENGTH 200

 

//#define USE_TRANSMITFILE // uncomment if you have Windows NT

 

volatile BOOL g_bListening = FALSE;

volatile UINT g_nPortServer = 80;

 

// Adjust to your programming environment accordingly...

CString g_strDirect = "F:\\mfcproject\\myex33a\\WebSite";

CString g_strIPServer;

// The default document, later, you need to create this file...

CString g_strDefault = "default.htm";

CBlockingSocket g_sListen;

 

BOOL Parse(char* pStr, char** ppToken1, char** ppToken2)

// really stupid parsing routine (must find two tokens, each followed by a space)

{

       *ppToken1 = pStr;

       char* pch = strchr(pStr, ' ');

       if(pch) {

              *pch = '\0';

              pch++;

              *ppToken2 = pch;

              pch = strchr(pch, ' ');

              if(pch) {

                     *pch = '\0';

                     return TRUE;

              }

       }

       return FALSE;

}

 

void LogRequest(LPVOID pParam, char* pch, CSockAddr sa)

{      // 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[1000];

       wsprintf(text1, "SERVER CONNECTION # %d: IP addr = %s, port = %d -- %s\r\n",

              g_nConnection, sa.DottedDecimal(), sa.Port(),  (const char*) strGmt);

       strcat(text1, pch);

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

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

}

 

CFile* OpenFile(const char* pName)

{

       // if it's really a directory, open the default HTML file

       CFileException e;

       CFile* pFile = new CFile();

       if(*pName == '/') pName++;

              CString strName = pName;

       if(pFile->Open(strName, CFile::modeRead, &e)) {

              return pFile;

       }

       if((e.m_cause == CFileException::accessDenied) || (e.m_cause == CFileException::badPath)) { // directory?

              int nLength;

              // add a / unless it's the "root" directory

              if((nLength = strName.GetLength()) > 1) {

                     if(strName[nLength - 1] != '/') {

                           strName += '/';

                     }

              }

              strName += g_strDefault;

              if(pFile->Open(strName, CFile::modeRead, &e)) {

                     return pFile;

              }

       }

       delete pFile;

       return NULL;

}

 

UINT ServerThreadProc(LPVOID pParam)

{

       CSockAddr saClient;

       CHttpBlockingSocket sConnect;

       char* buffer = new char[SERVERMAXBUF];

       char message[100], headers[500], request1[MAXLINELENGTH],  request2[MAXLINELENGTH];

       char hdrErr[ ] = "HTTP/1.0 404 Object Not Found\r\n"

              "Server: Inside Visual C++ SOCK01\r\n"

              "Content-Type: text/html\r\n"

              "Accept-Ranges: bytes\r\n"

              "Content-Length: 66\r\n\r\n" // WinInet wants correct length

              "<html><h1><body>HTTP/1.0 404 Object Not Found</h1></body></html>\r\n";

       char hdrFmt[ ] = "HTTP/1.0 200 OK\r\n"

              "Server: Inside Visual C++ EX34A\r\n"

              "Date: %s\r\n"

              "Content-Type: text/html\r\n"

              "Accept-Ranges: bytes\r\n"

              "Content-Length: %d\r\n";

       char html1[ ] = "<html><head><title>Inside Visual C++ \

              Server</title></head>\r\n"

              "<body><body background=\"/samples/images/usa1.jpg\">\r\n"

              "<h1><center>This is a custom home page</center></h1><p>\r\n"

              "<a href=\"/samples/iisdocs.htm\">Click here for iisdocs.htm.</a><p>\r\n"

              "<a href=\"/samples/disclaim.htm\">Click here for disclaim.htm.</a><p>\r\n";

              // custom message goes here

       char html2[] = "</body></html>\r\n\r\n";

       CString strGmtNow = CTime::GetCurrentTime().FormatGmt("%a, %d %b %Y %H:%M:%S GMT");

       int nBytesSent = 0;

       CFile* pFile = NULL;

       try {

              if(!g_sListen.Accept(sConnect, saClient)) {

                     // view or application closed the listing socket

                     g_bListening = FALSE;

                     delete [ ] buffer;

                     return 0;

              }

              g_nConnection++;

              ::SetCurrentDirectory(g_strDirect);

              AfxBeginThread(ServerThreadProc, pParam, THREAD_PRIORITY_NORMAL);

              // read request from client

              sConnect.ReadHttpHeaderLine(request1, MAXLINELENGTH, 10);

              LogRequest(pParam, request1, saClient);

              char* pToken1; char* pToken2;

              if(Parse(request1, &pToken1, &pToken2)) {

                 if(!stricmp(pToken1, "GET")) {

                     do { // eat the remaining headers

                       sConnect.ReadHttpHeaderLine(request2, MAXLINELENGTH, 10);

                       TRACE("SERVER: %s", request2);

                     }

                     while(strcmp(request2, "\r\n"));

                       if(!stricmp(pToken2, "/custom")) { // special request

                           // send a "custom" HTML  page

                           wsprintf(message, "Hi! you are connection #%d on IP %s, port %d<p>%s", g_nConnection,

                           saClient.DottedDecimal(), saClient.Port(), strGmtNow);

                           wsprintf(headers, hdrFmt, (const char*) strGmtNow, strlen(html1) + strlen(message) + strlen(html2));

                           // no If-Modified

                           strcat(headers, "\r\n"); // blank line

                           sConnect.Write(headers, strlen(headers), 10);

                           sConnect.Write(html1, strlen(html1), 10);

                           sConnect.Write(message, strlen(message), 10);

                           sConnect.Write(html2, strlen(html2), 10);

                           }

                           else if(strchr(pToken2, '?')) { // CGI request

                           // Netscape doesn't pass function name in a GET

                           TRACE("SERVER: CGI request detected %s\n", pToken2);

                           // could load and run the ISAPI DLL here

                           }

                           else { // must be a file assume this program has already

                           // set the default WWW directory

                           if((pFile = OpenFile(pToken2)) != NULL) {

                                  CFileStatus fileStatus;

                                  pFile->GetStatus(fileStatus);

                                  CString strGmtMod = fileStatus.m_mtime.FormatGmt("%a, %d %b %Y %H:%M:%S GMT");

                                  char hdrModified[50];

                                  wsprintf(hdrModified, "Last-Modified: %s\r\n\r\n", (const char*) strGmtMod);

                                  DWORD dwLength = pFile->GetLength();

                                  // Date: , Content-Length:

                                  wsprintf(headers, hdrFmt,  (const char*) strGmtNow, dwLength);

                                  strcat(headers, hdrModified);

                                  nBytesSent = sConnect.Write(headers, strlen(headers), 10);

                                  TRACE("SERVER: header characters sent = %d\n", nBytesSent);

                                  // would be a good idea to send the file only if the If-

                                  // Modified-Since date  were less than the file's m_mtime

                                  nBytesSent = 0;

#ifdef USE_TRANSMITFILE

              if(::TransmitFile(sConnect, (HANDLE) pFile->m_hFile, dwLength, 0, NULL, NULL, TF_DISCONNECT)) {

                     nBytesSent = (int) dwLength;

                     }

#else

              DWORD dwBytesRead = 0;

              UINT uBytesToRead;

              // send file in small chunks (5K) to avoid big memory alloc overhead

              while(dwBytesRead < dwLength) {

              uBytesToRead = min(SERVERMAXBUF, dwLength - dwBytesRead);

              VERIFY(pFile->Read(buffer, uBytesToRead) == uBytesToRead);

              nBytesSent += sConnect.Write(buffer, uBytesToRead, 10);

              dwBytesRead += uBytesToRead;

              }

#endif

              TRACE("SERVER: full file sent successfully\n");

              }

              else {

             // 404 Object Not Found

              nBytesSent = sConnect.Write(hdrErr, strlen(hdrErr), 10);

              }

              }

              }

              else if(!stricmp(pToken1, "POST")) {

                     do { // eat the remaining headers thru blank line

                           sConnect.ReadHttpHeaderLine(request2, MAXLINELENGTH, 10);

                           TRACE("SERVER: POST %s", request2);

                     }

                           while(strcmp(request2, "\r\n"));

                           // read the data line sent by the client

                           sConnect.ReadHttpHeaderLine(request2, MAXLINELENGTH, 10);

                           TRACE("SERVER: POST PARAMETERS = %s\n", request2);

                           LogRequest(pParam, request2, saClient);

                           // launch ISAPI DLL here?

                           // 404 error for now

                           nBytesSent = sConnect.Write(hdrErr, strlen(hdrErr), 10); }

                     else {

                           TRACE("SERVER: %s (not a GET or POST)\n", pToken1);

                           // don't know how to eat the headers

                     }

              }

              else {

                     TRACE("SERVER: bad request\n");

              }

              sConnect.Close(); // destructor can't close it

       }

       catch(CBlockingSocketException* pe) {

              LogBlockingSocketException(pParam, "SERVER:", pe);

              pe->Delete();

       }

       TRACE("SERVER: file characters sent = %d\n", nBytesSent);

       delete [ ] buffer;

       if(pFile) delete pFile;

       return 0;

}

 

Listing 20.

 

 

Continue on next Module...

 

 

Further reading and digging:

  1. DCOM at MSDN.

  2. COM+ at MSDN.

  3. COM at MSDN.

  4. Win32 process, thread and synchronization story can be found starting from Module R.

  5. MSDN MFC 7.0 class library online documentation.

  6. MSDN MFC 9.0 class library online documentation - latest version.

  7. MSDN Library

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | C++, MFC, Winsock & WinInet 5 | C++, MFC, Winsock & WinInet 7 | Download | Site Index |