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

Figure 75: Adding new 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:
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Win32 process, thread and synchronization story can be found starting from Module R.
MSDN MFC 9.0 class library online documentation - latest version.
Unicode and Multibyte character set: Story and program examples.