close

Trust Me!! Trust You!!


  • Blog
  • Local Log
  • Tag Cloud
  • Key Log
  • Guestbook
  • RSS Feed
  • Write a Post
  • Admin

혹시 블로그 스킨이 깨져 보이시나요? 최신버전의 Internet Explorer(Windows용), Opera, Firefox를 사용해보세요.

ASP 의 BinaryRead() 혹은 ISAPI ReadClient() 가 때때로 블록된다.

웹 프로그래밍
2007/04/10 01:51
 

현상

HTTP Post 메소드를 사용하는 경우 서버 어플리케이션은 ASP 의 BinaryRead 에 해당하는 ISAPI 함수인 ReadClient 를 이용하여 클라이언트로부터 HTTP 본문에 있는 데이터를 읽는다. 그런데, POST 의 사이즈가 큰 경우 클라이언트 사용자가 upload 를 취소하게 되면 ReadClient 혹은 BinaryRead 를 호출한 서버 프로그램이 blocking 되는 경우가 있다.

위로 가기

원인

ReadClient 기술을 읽어보면 다음과 같은 문장이 있다.
If the size specified in lpdwSize is greater than the number of 
bytes available to be read, it is possible ReadClient will block until 
the data arrives.

ReadClient 는 일단 호출이 되고나면 한바이트의 데이터라도 도착하지 않으면 block 된다. 예외의 경우에 대한 다음 문장이 역시 기술되어 있다.
	If the socket on which the server is listening to the client is closed, 
	ReadClient will return TRUE, but with zero bytes read.
소켓이 끊기는 경우에 한하여 Readclient 는 반환된다. 문제는 IE 와 IIS 가 사용하고 있는 HTTP 프로토콜에 Keep Alive 기능이 추가된데 있다. Keep Alive 는 연속되는 HTTP Request/Response 가 동일한 socket 연결을 계속 사용한다는 것이다. 이로 인해 client 가 POST 를 취소해도 socket 은 끊지 않는 경우가 발생한다.

위로 가기

해결 방법

이 문제는 ASP BinaryRead 의 메모리 누수 hotfix 에 의해 문제가 해결되지 않습니다. 하지만 다음과 같이 코딩을 하여 문제를 해결할 수 있을 것입니다. 주의할 점은 ASP Request 오브젝트의 BinaryRead 를 사용하는 경우 IIS 는 일정량의 데이터가 도착하기 전에는 데이터를 서버 어플리케이션에 전달하지 않는다는 점이다. 따라서 클라이언트가 이에 미치지 않는 소량의 데이터를 upload 하는 중간에 작업을 취소한 경우 서버가 역시 block 될 수 있다는 것이다. 이는 일정량의 데이터를 upload 하기 전에는 upload 를 취소하지 못하도록 하여 문제를 막을 수 있다.

client 와 서버가 모두 고객이 커스터마이징한 코드가 적용가능한 경우, HTTP 상에 다음과 같은 프로토콜을 적용하여 문제를 피해갈 수 있다.

전송 순서:

					
Client					Server
-------------------------------------------------------------------
HTTP Request header 				ReadClient

4 byte size indicator of HTTP Body part 1/n	ReadClient
	if size == 0 then break
HTTP Body part 1/n				ReadClient

4 byte size indicator of HTTP Body part 2/n	ReadClient
	if size == 0 then break
HTTP Body part 2/n				ReadClient

4 byte size indicator of HTTP Body part 3/n	ReadClient
	if size == 0 then break
HTTP Body part 3/n				ReadClient

4 byte size indicator of HTTP Body part 4/n	ReadClient
	if size == 0 then break
HTTP Body part 4/n				ReadClient

...

4 byte size indicator of HTTP Body part 5/n	ReadClient
	if size == 0 then break
HTTP Body part 5/n				ReadClient

서버 샘플코드

				
STDMETHODIMP CRead::BinaryRead()
{
	// TODO: Add your implementation code here
	VARIANT	varCount;
	VARIANT varData;
	HRESULT hr;

	while(1) {
		varCount.vt = VT_I4;
		varCount.lVal = 4;
		hr = m_piRequest->BinaryRead(&varCount, &varData);

		if ( FAILED(hr) ) {
#ifdef	_DEBUG
		FILE	*fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:Error for BinaryRead, hr = %x", hr);
			fflush(fp);
			fclose(fp);
		}
#endif
			return E_FAIL;
		}

		if ( varData.vt != (VT_ARRAY | VT_UI1) ) {
#ifdef	_DEBUG
		FILE	*fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:Error varData.vt != VT_ARRAY | 
VT_UI1");
			fflush(fp);
			fclose(fp);
		}
#endif
			return E_FAIL;
		}

		SAFEARRAY *pSafeArray;
		pSafeArray = varData.parray;
		BYTE	*byteArray;

		hr = SafeArrayAccessData(pSafeArray, (void**)&byteArray);

		if ( FAILED(hr) ) {
#ifdef	_DEBUG
			FILE *fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:Error for SafeArrayAccessData, hr=%x\n", 
hr);
			fflush(fp);
			fclose(fp);
		}
#endif
			SafeArrayUnaccessData(pSafeArray);
			return E_FAIL;
		}
//		cElements = pSafeArray->rgsabound->cElements;
		char	size[5];
		size[0] = byteArray[0];
		size[1] = byteArray[1];
		size[2] = byteArray[2];
		size[3] = byteArray[3];
		size[4] = 0;
		int	nSize = atoi(size);

#ifdef	_DEBUG
		FILE *fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:nSize = %d\n", nSize);
			fflush(fp);
			fclose(fp);
		}
#endif
		SafeArrayUnaccessData(pSafeArray);

		if ( nSize == 0 ) {
#ifdef	_DEBUG
		FILE *fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:nSize is 0, so break this loop\n");
			fflush(fp);
			fclose(fp);
		}
#endif
			break;
		}

		varCount.vt = VT_I4;
		varCount.lVal = nSize;
		hr = m_piRequest->BinaryRead(&varCount, &varData);

		if ( FAILED(hr) ) {
#ifdef	_DEBUG
		FILE *fp;
		if ( (fp = fopen(LOG_FILE, "a")) != NULL ) {
			fprintf(fp, "BinaryRead:Failed for BinaryRead, hr = %x\n", hr);
			fflush(fp);
			fclose(fp);
		}
#endif
		}
	}

	return S_OK;
}

클라이언트 샘플코드

클라이언트는 WinInet 을 사용하므로 wininet.lib 를 링크한다.
#include <Windows.h>
#include <WinINet.h>
#include <stdio.h>

BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize);
#define BUFFSIZE 500

void main( int argc, char **argv )
{
	DWORD dwPostSize;

	if (argc < 4)
	{
		printf("Usage: Bigpost <Size> <Server> <Path>\n");
		printf("<Size> is the number of KB to POST\n");
		printf("<Server> is the server to POST to\n");
		printf("<Path> is the virtual path to POST to\n");
		exit(0);
	}

	if ( ((dwPostSize = strtoul(argv[1],NULL,10)) == 0) || 
(dwPostSize >= 2047999) )
	{
		printf("%s is invalid size.  Valid sizes are from 1 
to 2047999\n", argv[1]);
		exit(0);
	}

	printf( "Test of POSTing %luKB with WinInet\n", dwPostSize);

	dwPostSize *= 1024;  // Convert KB to bytes

	HINTERNET hSession = InternetOpen( "HttpSendRequestEx", 
INTERNET_OPEN_TYPE_PRECONFIG,
		NULL, NULL, 0);
	if(!hSession)
	{
		printf("Failed to open session\n");
		exit(0);
	}

////ORIGINAL SOURCE CODES BEGIN////////////////////////////////////////////////////////
	HINTERNET hConnect = InternetConnect(hSession, argv[2], 
INTERNET_DEFAULT_HTTP_PORT,
		NULL, NULL, INTERNET_SERVICE_HTTP,NULL, NULL);
	if (!hConnect)
		printf( "Failed to connect\n" );
	else
	{
		HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", argv[3], 
			NULL, NULL, NULL, 
INTERNET_FLAG_NO_CACHE_WRITE, 0);
		if (!hRequest)
			printf( "Failed to open request handle\n" );
		else
		{
			if(UseHttpSendReqEx(hRequest, dwPostSize))
			{	
				char pcBuffer[BUFFSIZE];
				DWORD dwBytesRead;

				printf("\nThe following was returned by the 
server:\n");
				do
				{	dwBytesRead=0;
					if(InternetReadFile(hRequest, pcBuffer, 
BUFFSIZE-1, &dwBytesRead))
					{
						pcBuffer[dwBytesRead]=0x00; // 
Null-terminate buffer
						printf("%s", pcBuffer);
					}
					else
						printf("\nInternetReadFile failed");
				}while(dwBytesRead>0);
				printf("\n");
			}
			if (!InternetCloseHandle(hRequest))
				printf( "Failed to close Request handle\n" );
			else 
				printf( "Succeeded to close Request handle\n" );
		}
/*		
		if(!InternetCloseHandle(hConnect))
			printf("Failed to close Connect handle\n");
		else
			printf("Succeded to close Connect handle\n");
*/
	}
	printf( "\nFirst sending Finished....\n" );

////ORIGINAL SOURCE CODES ENDS////////////////////////////////////////////////////////
	/*
	if( InternetCloseHandle( hSession ) == FALSE )
		printf( "Failed to close Session handle\n" );

	hSession = InternetOpen( "HttpSendRequestEx", 
INTERNET_OPEN_TYPE_PRECONFIG,
		NULL, NULL, 0);

	if(!hSession)
	{
		printf("Failed to open session\n");
		exit(0);
	}
	*/
////DUPLICATED SOURCE CODES BEGIN////////////////////////////////////////////////////////
	//HINTERNET 
	/*
	hConnect = InternetConnect(hSession, argv[2], INTERNET_DEFAULT_HTTP_PORT,
		NULL, NULL, INTERNET_SERVICE_HTTP,NULL, NULL);
	if (!hConnect)
		printf( "Failed to connect\n" );
	else
		*/
	{
		HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", argv[3], 
			NULL, NULL, NULL, 
INTERNET_FLAG_NO_CACHE_WRITE, 0);
		if (!hRequest)
			printf( "Failed to open request handle\n" );
		else
		{
			if(UseHttpSendReqEx(hRequest, dwPostSize))
			{	
				char pcBuffer[BUFFSIZE];
				DWORD dwBytesRead;

				printf("\nThe following was returned by the server:\n");
				do
				{	dwBytesRead=0;
					if(InternetReadFile(hRequest, pcBuffer, 
BUFFSIZE-1, &dwBytesRead))
					{
						pcBuffer[dwBytesRead]=0x00; // 
Null-terminate buffer
						printf("%s", pcBuffer);
					}
					else
						printf("\nInternetReadFile failed");
				}while(dwBytesRead>0);
				printf("\n");
			}
			if (!InternetCloseHandle(hRequest))
				printf( "Failed to close Request handle\n" );
			else
				printf( "Succeded to close Request handle\n" );
		}

		if(!InternetCloseHandle(hConnect))
			printf("Failed to close Connect handle\n");
		else
			printf("Succeeded to close Connect handle\n");

	}
	printf( "\nSecond sending Finished....\n" );
////DUPLICATED SOURCE CODES ENDS////////////////////////////////////////////////////////
	if( InternetCloseHandle( hSession ) == FALSE )
		printf( "Failed to close Session handle\n" );

	printf( "\nFinished.\n" );
}



BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostDataSize)
{
	INTERNET_BUFFERS BufferIn;
	DWORD dwBytesWritten;
	int n;
	BYTE pBuffer[1024];
	BOOL bRet;
	DWORD	dwPostSize;
	dwPostSize = dwPostDataSize + (dwPostDataSize/1024)*4;
	dwPostSize += 4;	// for the last data length = 0

	BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or error will 
occur
    BufferIn.Next = NULL; 
    BufferIn.lpcszHeader = NULL;
    BufferIn.dwHeadersLength = 0;
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;                
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwPostSize; // This is the only member used other 
than dwStructSize
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0;

    if(!HttpSendRequestEx( hRequest, &BufferIn, NULL, 0, 0))
    {
        printf( "Error on HttpSendRequestEx %d\n",GetLastError() );
        return FALSE;
    }

	FillMemory(pBuffer, 1024, 'D'); // Fill buffer with data

	bRet=TRUE;
	//Changed by YOO
	char pSize[4];
	pSize[0] = '1';
	pSize[1] = '0';
	pSize[2] = '2';
	pSize[3] = '4';

	for(n=1; n<=(int)10 && bRet; n++)//(int)dwPostSize/1024 && bRet; n++)
	{
		if(!InternetWriteFile( hRequest, &pSize, 4, &dwBytesWritten))
			printf("\nerror for writeing size");

		if(bRet=InternetWriteFile( hRequest, pBuffer, 1024, &dwBytesWritten))
			printf( "\r%d bytes sent.", n*1024);
	}

	pSize[0] = '0';
	pSize[1] = '0';
	pSize[2] = '0';
	pSize[3] = '0';
	if (InternetWriteFile( hRequest, &pSize, 4, &dwBytesWritten))
		printf("\nwrote nSize = 0");

	printf("\nafter InternetWriteFile loop, bRet = %d", bRet);

	if(!bRet)
	{
        printf( "\nError on InternetWriteFile %lu\n",GetLastError() );
        return FALSE;
    }

	printf("\nWe gonna do HttpEndRequest");

	//Changed by YOO
//    if(!HttpEndRequest(hRequest, NULL, 0, 0))
//	if(!HttpEndRequest(hRequest, NULL, HSR_SYNC, 0))
//    {
//        printf( "Error on HttpEndRequest %lu \n", GetLastError());
//        return FALSE;
//    }

	printf("\nOK I'll return TRUE");
	return TRUE;
}
이올린에 북마크하기(0) 이올린에 추천하기(0)
No received trackback. / Comment 2

Trackback Address :: http://viper150.cafe24.com/trackback/24

You can also say.

Prev 1 ... 272 273 274 275 276 277 278 279 280 ... 298 Next
블로그 이미지
이것저것 불펌금지도 퍼다가 담습니다. 외부에 비공개된 페이지 입니다. By. 어른왕자

카테고리

  • 전체 (298)
    • 사는 이야기 (115)
    • 웹 프로그래밍 (102)
    • App 프로그래밍 (22)
    • IT 뉴스&기타 (22)
    • 박한별 (4)
    • 역사&기타지식 (9)

태그목록

  • hiddenmenu
  • 아이유
  • benchmark
  • 자바스크립트 마우스 막기
  • vga성능
  • DB2
  • Dialog Base
  • 만화
  • 마우스
  • 히든메뉴
  • RequestMapping
  • 몰카
  • preventDefault
  • java api
  • 의료민영화
  • BrowserHelperObjects
  • 웃김
  • 락산
  • 명언
  • 홍수아
  • 아이유착각
  • 팝
  • IIS
  • 아이폰
  • ligagg
  • substr
  • 광개토대왕
  • 견찰
  • 아이폰6
  • 붕당

최근에 올라온 글

  • 보험사의 조정신청 대응방법.
  • 어느 천재의 앞선 시선.
  • [병맛더빙] 누구게..... (1)
  • 韓경제 `회색 코뿔소` 상황...
  • SVN Connector 설치 URL.
  • 군대를 가지 않는 서울대생.
  • “운은 하늘의 귀여움 받는...
  • 목장에서 알바하다가 캐스...
  • [펌]믿고 거르는 관상.
  • 하루에 1세트씩 하면 좋다...

최근에 달린 댓글

  • <p align="center"><a href="h... 미구구나 01/17
  • <p align="center"><a href="h... 미구구나 01/17
  • <p><img src="https://i.imgur... 덤프 01/13
  • <p><img src="https://i.imgur... 덤프 01/13
  • <p><img src="https://i.imgur... 덤프 01/13

최근에 받은 트랙백

  • công ty may đồng phục. công ty may đồng phục 01/08
  • Israelnightclub`s recent blo... Israelnightclub`s recent blo.. 01/06
  • Suggested Browsing. Suggested Browsing 01/06
  • similar site. similar site 01/06
  • לאתר הבית שלנו. לאתר הבית שלנו 01/06

글 보관함

  • 2019/03 (1)
  • 2018/12 (1)
  • 2018/09 (1)
  • 2018/08 (1)
  • 2018/02 (1)

달력

«   2021/01   »
일 월 화 수 목 금 토
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

링크

  • Total : 261779
  • Today : 41
  • Yesterday : 50
Tattertools
Eolin
rss

어른왕자's blog is powered byTattertools1.1.2.2 : Animato