posted by 경이's 2020. 2. 25. 19:02

std::string 에서 문자열을 자르는 함수 substr 로 문자열을 자를 시, 바이트 단위로 문자열을 자르게 되어 한글의 경우 깨지는 현상이 발생한다. 

 

따라서 UTF-8 문자열의 바이트 시퀀스를 보고 어디까지가 한문자로 표현되는지를 판단 후 문자열을 잘라야한다.

아래는 예시로 작성된 코드이다.

std::string CTraceModule::Utf8SubString(std::string strUtf8String, int nLimitLength)
{
	std::size_t charCnt = 0;
	std::string strRetVal = strUtf8String;
	std::string::const_iterator itBegin = strRetVal.begin(), itEnd = strRetVal.end();

	try
	{
		while (itBegin != itEnd)
		{
			unsigned char c = *itBegin;
			int nByte = 0;

			// 한 문자가 몇 바이트로 이루어져 있는지 확인한다.
			if ((c & 0x80) == 0)    nByte = 1;
			else if ((c & 0xE0) == 0xC0) nByte = 2;
			else if ((c & 0xF0) == 0xE0) nByte = 3;
			else if ((c & 0xF8) == 0xF0) nByte = 4;
			else
			{
				// Invalid UTF-8
				break;
			}

			if (itEnd - itBegin < nByte)
			{
				// String too short
				break;
			}

			// 연속된 바이트인지 확인
			for (int i = 1; i < nByte; ++i)
			{
				// 상위 2비트를 0x80 과 비교, 동일하지 않으면 연속된 바이트가 아님 (한 문자가 아님)
				if ((itBegin[i] & 0xC0) != 0x80)
				{
					// Unexpected byte sequence
					return strRetVal;
				}
			}

			itBegin += nByte;
			charCnt++;

			// 제한 길이와 문자길이가 동일할 경우 문자열을 자른다.
			if (nLimitLength == charCnt)
			{
				strRetVal.erase(itBegin, itEnd);
				return strRetVal;
			}
		}
	}
	catch (...)
	{
		// Exception
	}

	return strRetVal;
}

'C++, MFC' 카테고리의 다른 글

[C++] 프로세스 이름으로 PID 및 사용자 이름 얻기  (2) 2020.02.03
Etc
posted by 경이's 2020. 2. 12. 18:32

IIS 로 구성된 웹 페이지에 과부하 테스트를 해야하는 상황에서 Stress Tool 을 찾아보다가 Freeware 인 Webserver Stress Tool 8 이라는 도구를 알게되었다.

 

사용법도 간단하고 테스트 결과를 리포트 형식으로 출력해줘서 과부하 테스트 시에 사용하고 있다.

 

해당 툴은 아래 링크에서 다운받을 수 있다.

https://www.paessler.com/tools/webstress

 

Webserver Stress Tool - Performance, stress & load test.

By simulating the HTTP requests generated by hundreds or even thousands of simultaneous users, you can test your web server performance under normal and excessive loads to ensure that critical information and services are available at speeds your end-users

www.paessler.com

 

프로그램을 설치 후 실행하면 다음과 같은 화면이 전시된다.

 

왼쪽의 카테고리 중 Test Type 에서 CLICKS, TIME, RAMP 등 원하는 테스트 옵션을 선택 가능하며 User Simulation 설정을 통해 몇명의 사용자를 생성하여 접속하게 할 것인지 입력받을 수 있다.

 

그다음 카테고리인 URLs 를 살펴보면 URL 을 입력할 수 있는 창이 보여진다.

Name, Click Delay, URL, Post data, Username, Password 등 URL 관련 정보를 입력이 가능한데

URL 만 입력 후 상단 툴바에 [Start Test] 를 누르면 테스트가 실행되는 것을 확인할 수 있다.

URL Pattern 의 경우 기본 Simple URL Sequence 를 선택한다.

Custom URL Script 는 사용해보질 않아서 해당 항목에 대한 설명은 패스..

URL 입력 시, 로그인에서 부터 특정행위 후 로그아웃 까지 일련의 동작을 반복해서 테스트 하고 싶을 때엔 URL Recorder 를 선택한다.

URL Recorder 창에서 테스트할 URL 을 입력 후 Go! 버튼을 누르면 해당 페이지에 접속하게 된다. 그 후 로그인 및 원하는 행위를 하면 자연스레 아래에 접속한 URL 과 POST-DATA 가 입력된다. 마지막에는 상단의 [Save URLs]를 선택.

[Saver URLs] 까지 선택하고 나면 아래와 같이 URL 정보가 입력된 것을 확인할 수 있다.

이렇게 테스트를 하게되면 2000명의 사용자가 모두 admin / admin123 이라는 계정을 사용해서 해당 URL에 접속하게 된다. 하지만 각각 다른 사용자로 접속을 원할 경우엔 [URL Recorder] 옆에 있는 [Data Merging] 을 선택한다.

선택하게 되면 아래와 같은 창이 나타나는데 아래의 Replace Placeholders 를 선택하고 랜덤으로 선택을 원할 경우에 Randomly select data 에 체크한다. 그 후 오른쪽의 [Edit "data.data"] 버튼을 선택.

버튼을 선택하면 또다른 창이 나타는데 여기에 여러 ID 와 PW 를 입력하여 테스트 USER 가 이중에서 랜덤으로 ID와 PW 를 선택해 해당 URL에 접속하도록 했다.

테스트 경우가 많지 않을 경우, 직접 입력해도 상관없지만 그 갯수가 많을 경우엔 C:\Program Files (x86)\Webserver Stress Tool 8\data.dat 파일을 직접 수정하는게 빠를 것이다. 나도 2000개의 데이터를 넣어놨는데 입력하는 시간이 아까워 배치파일로 반복데이터를 출력해 data.dat 에 직접 쓰는 방법으로 데이터를 입력했다.

데이터를 모두 입력 후에는 아래와 같이 iID=admin&iPW=admin123 이라고 적혀있던 POST data 부분을 iID=@1@&iPW=@2@ 로 변경해 입력해주면 설정은 끝이 난다. 

그 외에 Browser Settings 이나 Options, Test Results 쪽은 특별히 어려운 부분이 없어서 넘어가도록 하겠다. 

설정 후 상단 툴바의 [Start Test]를 누르면 테스트가 진행된다.

테스트 결과는 PC 성능에 따라 차이가 있을 수 있음

'Etc' 카테고리의 다른 글

[OpenSSL] Windows 에서 OpenSSL 빌드하기  (0) 2020.02.04
Etc
posted by 경이's 2020. 2. 4. 19:54

이번에 OpenSSL 을 직접 빌드하여 사용하게 되었는데 버전에 따라 빌드하는 방법에 조금 차이가 있기에 정리한다.

 

빌드 환경은 Windows 10 64bit / Visual Studio 2008 에서 빌드하였다.

 

OpenSSL 소스코드 다운로드 : https://github.com/openssl/openssl/releases

 

openssl/openssl

TLS/SSL and crypto library. Contribute to openssl/openssl development by creating an account on GitHub.

github.com

내가 빌드한 SSL 버전은 1.0.2u 버전과 1.1.1d 버전이다.

 

두 버전 모두 Perl 이 설치되어 있어야 빌드가 가능하며, 1.1.1d 버전에서는 Perl 을 x86 버전으로 설치해야 빌드가 가능했다.

 

Perl 다운로드 : https://downloads.activestate.com/ActivePerl/releases/5.26.0.2600/

 

1. OpenSSL 1.0.2u 버전 빌드

   - 다운받은 OpenSSL 소스코드를 압축을 풀고 C:\ 경로에 넣는다.

   - Perl 설치 후 Visual Studio 명령 프로프트 창을 실행한다.
     (x86 빌드를 원할 시에는 명령 프롬프트도 x86 버전 실행)

   - 커맨드 창에서 OpenSSL 소스코드가 있는 위치로 이동 후 Configure 명령어를 수행
     (원하는 동작에 맞는 명령 수행)

     ex) 
        1. x86 static library
           perl Configure VC-WIN32 --openssldir=C:\OpenSSL-x86 no-shared no-asm threads no-idea no-mdc2 no-rc5

        2. x86 static debug library
           perl Configure debug-VC-WIN32 --openssldir=C:\OpenSSL-x86-debug no-shared no-asm threads no-idea no-mdc2 no-rc5

        3. x64 static library
           perl Configure VC-WIN64A --openssldir=C:\OpenSSL-x64 no-shared no-asm threads no-idea no-mdc2 no-rc5

        4. x64 static debug library
           perl Configure debug-VC-WIN64A --openssldir=C:\OpenSSL-x64-debug no-shared no-asm threads no-idea no-mdc2 no-rc5

 

         ※ 참고로 --openssldir=C:\OpenSSL 과 같이 입력하면 "C:\OpenSSL" 디렉토리에 라이브러리가 설치됨
         ※ no-idea no-mdc2 no-rc5 는 암호화 알고리즘 라이센스 문제로 인하여 빌드에서 제외

 

   - Makefile 생성 (NASM을 사용하는 경우)

         1. x86
            > 어셈블리어를 사용하지 않는 경우       ms\do_ms.bat          
            > NASM 어셈블리어를 사용하는 경우    ms\do_nasm.bat    
            > MASM 어셈블리어를 사용하는 경우   ms\do_masm.bat
         2. x64
            > 어셈블리어를 사용하지 않는 경우     ms\do_win64a.bat
         3. Itanium
            > 어셈블리어를 사용하지 않는 경우 ms\do_win64i.bat

 

   - 빌드
         1. 동적 라이브러리 빌드 : C:\openssl-1.0.2.t> nmake -f ms\ntdll.mak
         2. 정적 라이브러리 빌드 : C:\openssl-1.0.2.t> nmake -f ms\nt.mak

 

   - 테스트

         1. 동적 라이브러리 빌드 : C:\openssl-1.0.2.t> nmake -f ms\ntdll.mak test
         2. 정적 라이브러리 빌드 : C:\openssl-1.0.2.t> nmake -f ms\nt.mak test
       - 마지막에 "passed all tests" 라는 메세지가 뜨면 성공한 것.

 

   - 컴파일 및 설치
         1. 정적 라이브러리 빌드 인 경우 : nmake -f ms\nt.mak install
         2. 동적 라이브러리 빌드 인 경우 : nmake -f ms\ntdll.mak install

 

 

2. OpenSSL 1.1.1d 버전 빌드

   - 위의 1.0.2u 버전과 거의 동일하지만 소스코드 내 ms 폴더 하위에 배치파일이 존재하지 않아 makefile 을 생성할 수 가 없다.

   - 1.1.1d 를 빌드하기 위해서는 nasm 이 설치되어야 한다.

   - nasm 다운로드 :  https://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D

   - 위에서 말한 것과 마찬가지로 Perl 이 x86 버전으로 설치되어야만 가능 (이유는 모르겠다..;)

   - 1.0.2u 버전과 동일하게 소스코드를 C:\ 에 넣고 Visual Studio 명령 프롬프트를 사용하여 소스코드가 위치한 곳으로 이동한다.

   - 위와 동일하게 원하는 빌드 형태의 Configure 명령어를 수행한다.

   - Configure 설정 완료 후 커맨드 창에 nmake 를 입력한다.

   - 빌드가 완료되면 nmake test 로 테스트를 수행할 수 있으며, nmake install 명령어로 설치할 수 있다.

   - 설치 경로는 x86 일 경우 : C:\Program Files (x86)\OpenSSL

   - x64 일 경우 : C:\Program Files\OpenSSL 에 설치된다.

 

첨부파일 내용 : 실제 정적 빌드한 라이브러리 파일

Openssl_1.0.2u.7z
4.82MB
Openssl_1.1.1d.7z
9.42MB

'Etc' 카테고리의 다른 글

[IIS] 과부하 테스트 / Webserver Stress Tool 8 사용법  (0) 2020.02.12
posted by 경이's 2020. 2. 3. 18:17

작업관리자에 보면 프로세스에 대한 다양한 정보가 있는데 이 중에서 프로세스의 이름으로 PID 및 사용자 이름을 확인하는 코드를 작성해 보았다.

[작업관리자]

 

우선 프로세스 이름을 이용하여 PID 및 프로세스의 핸들을 가져오는 함수

DWORD FindProcInfo(std::wstring strProcName, std::wstring& stdProcUserName) 
{ 
    DWORD dwRetPID = 0; 
    std::wstring strUpperProcName = strProcName; 

    // 입력받은 프로세스 이름을 모두 대문자로 변환 
    std::transform(strUpperProcName.begin(), strUpperProcName.end(), 
		strUpperProcName.begin(), toupper); 

    // 스냅샷 생성 
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
    if ((INVALID_HANDLE_VALUE == hSnapshot) || (NULL == hSnapshot)) 
    { 
        std::cout << "CreateToolhelp32Snapshot Fail" << std::endl; 
        return -1; 
    } 

    PROCESSENTRY32 pe32; 
    pe32.dwSize = sizeof(PROCESSENTRY32); 
    std::wstring strTmpProcessName; 

    if (Process32First(hSnapshot, &pe32)) 
    { 
        //프로세스 검색 시작 
        do 
        { 
            strTmpProcessName = pe32.szExeFile; 
            std::transform(strTmpProcessName.begin(), strTmpProcessName.end(), 
				strTmpProcessName.begin(), toupper); 

            // 프로세스 목록 중 입력받은 프로세스 이름과 동일한 프로세스를 찾는다. 
            if (0 != wcscmp(strTmpProcessName.c_str(), strUpperProcName.c_str())) 
            { 
            	continue; 
            } 

            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, pe32.th32ProcessID); 
            if ((INVALID_HANDLE_VALUE == hProcess) || (NULL == hProcess)) 
            { 
                std::wcout << L"OpenProcess Fail, ErrorCode : " << GetLastError() << std::endl; 
                continue; 
            } 
            dwRetPID = pe32.th32ProcessID; 

            DWORD dwRetVal = ERROR_SUCCESS; 

            // 프로세스의 사용자 이름을 얻는다. 
            dwRetVal = GetProcessUserName(hProcess, stdProcUserName); 
            if (ERROR_SUCCESS != dwRetVal) 
            { 
            	std::wcout << L"GetProcessUserName Fail, ErrorCode : " << dwRetVal << std::endl; 
            } 
            CloseHandle(hProcess); 
            break; 

        } while (Process32Next(hSnapshot, &pe32));
    }

    CloseHandle(hSnapshot); 
    return dwRetPID; 
}

 

다음은 사용자 이름을 가져오는 함수

DWORD GetProcessUserName(HANDLE hProcess, std::wstring& strUserName) 
{ 
 	DWORD dwRetVal = ERROR_SUCCESS, dwResult = ERROR_SUCCESS; 
  	HANDLE hProcessToken = NULL; 
  	PTOKEN_USER pUserToken = NULL; 

  	if ((NULL == hProcess) || (INVALID_HANDLE_VALUE == hProcess)) 
  	{ 
		dwRetVal = ERROR_INVALID_PARAMETER; 
		return dwRetVal; 
  	} 

  	try 
  	{ 
		// 프로세스 토큰을 오픈한다. 
		if ((FALSE == OpenProcessToken(hProcess, TOKEN_READ, &hProcessToken)) || 
			(NULL == hProcessToken) || (INVALID_HANDLE_VALUE == hProcessToken)) 
		{ 
			dwResult = GetLastError(); 
			throw dwResult; 
		} 

		// 토큰 정보를 획득하기 위해 필요한 크기를 구한다. 
		DWORD dwProcessTokenInfoAllocSize = 0; 
		if (FALSE == GetTokenInformation(hProcessToken, TokenUser, NULL, 0, 
			&dwProcessTokenInfoAllocSize)) 
		{ 
			dwResult = GetLastError(); 
			if (ERROR_INSUFFICIENT_BUFFER != dwResult) 
			{ 
				throw dwResult; 
			} 
		} 

		// 토큰 정보를 얻기위해 필요한 크기만큼 메모리를 할당한다. 
		pUserToken = reinterpret_cast(new BYTE[dwProcessTokenInfoAllocSize]); 
		if (NULL == pUserToken) 
		{ 
			dwResult = GetLastError(); 
			throw dwResult; 
		} 

		// 사용자 정보를 얻기위해 프로세스의 토큰 정보를 획득한다. 
		if (FALSE == GetTokenInformation(hProcessToken, TokenUser, pUserToken, 
				dwProcessTokenInfoAllocSize, &dwProcessTokenInfoAllocSize)) 
		{ 
			dwResult = GetLastError(); 
			throw dwResult; 
		} 

		SID_NAME_USE   snuSIDNameUse; 
		TCHAR          szUser[MAX_PATH] = { 0 }; 
		DWORD          dwUserNameLength = MAX_PATH; 
		TCHAR          szDomain[MAX_PATH] = { 0 }; 
		DWORD          dwDomainNameLength = MAX_PATH; 

		// 사용자의 SID 를 이용하여 사용자 이름을 얻는다. 
		if (FALSE == LookupAccountSid(NULL, pUserToken->User.Sid, szUser, 
			&dwUserNameLength, szDomain, &dwDomainNameLength, &snuSIDNameUse)) 
		{ 
			dwResult = GetLastError(); 
			throw dwResult; 
		} 

		// 사용자 이름을 저장한다. 
		strUserName = szUser; 

		if (strUserName.empty()) 
		{ 
			dwRetVal = ERROR_INVALID_DATA; 
		} 
  	} 
  	catch (...) 
  	{ 
		dwRetVal = GetLastError(); 
  	} 

  	if (NULL != pUserToken) 
  	{ 
		delete[] pUserToken; 
  	} 

  	if (NULL != hProcessToken) 
  	{ 
		CloseHandle(hProcessToken); 
  	} 

  	return dwRetVal; 
}

 

실행예제 메인 함수

int main() 
{ 
	while (TRUE) 
	{ 
		WCHAR szProcName[MAX_PATH] = { 0, }; 
		DWORD dwPID = 0; 
		std::wstring strProcName, strProcUserName; 

		std::wcout << L"Please Enter Process Name (Exit = 0) : "; 
		std::wcin >> szProcName; 

		if (0 == wcscmp(szProcName, L"0")) 
		{ 
			break; 
		} 

		strProcName = szProcName; 

		dwPID = FindProcInfo(strProcName, strProcUserName); 
		if (dwPID > 0) 
		{ 
			std::wcout << L"Process ID : " << dwPID << std::endl; 
			std::wcout << L"Process Name : " << strProcName.c_str() << std::endl; 
			std::wcout << L"Process User Name : " << strProcUserName.c_str() << std::endl; 
		} 
		else 
		{ 
			std::wcout << L"FindProcInfo Fail" << std::endl; 
		} 

		std::wcout << std::endl; 
	} 

	return 0; 
}

 

실행결과

[실행결과]

'C++, MFC' 카테고리의 다른 글

[C++] UTF-8 문자열을 문자 단위로 자르기  (0) 2020.02.25