- 블로그 이전 하였습니다. http://www.withdev.com 으로 오세요. -
제가에서 개발한 파일 서버의 스트레스 테스트용으로 간단하게 만든 프로그램입니다.
혹시 통신과 파일 전송에 대해 궁금하신분이 있을것 같아 한번 강좌식으로 한번 포스팅 해봅니다.(반응이 좋으면 다른 테스트 프로그램을 만들시 계속적으로 포스팅 해보겠습니다.)
일단 작업 환경은 다음과 같습니다.
UI는 다이얼로그 베이스로 몇가지 입력 받아 처리 할 수 있게 만들어 놓았습니다.
UI설명은 일단 하지 않겠습니다.
먼저 프로그램 실행 시 다이얼로그 베이스이기에 OnInitDialog()가 호출 될 것입니다.
호출 시 여러 쓰레드에서 동시 전송으로 스트레스 테스트 하기 때문에 이 쓰레드를 관리 하는 리스트를 초기화합니다.
필요한 정보를 입력 받아 시작 버튼을 클릭 합니다. 시작버튼이 눌러 지면 지정된 스레드 갯수 만큼 생성 시키고 생성된 쓰레드는 쓰레드 리스트에 추가를 합니다.
위 코드를 보면 알겠지만 타이머로 0.5 간격으로 쓰레드가 종료 되었는지 확인을 합니다. 타이머 코드를 보겠습니다.
위 처럼 쓰레드 종료 코드를 확인 하여 쓰레드 종료 확인을 하고 리스트에서 할당 되어 있는 메모리를 해제 해줍니다.
다이얼로그에서는 위와 같은 일만 합니다. 주 작업은 쓰레드에서 이루어집니다. 쓰레드 함수를 지금부터 보겠습니다.
주석으로 간략 하게 설명을 드렸습니다. 전 처음 통신프로그램을 만들때가 리눅스에 만들다 보니 코드를 보면 버클리 Socket을 사용하여 만들었습니다. 좀더 세밀하게 조절 할 수 있어 선호하는 방식입니다. 버클리 방식은 책에 많이 있어 간략 하게 말씀을 드리겠습니다.
이상 간단한 파일 전송 프로그램입니다. 아직 통신프로그램에 대해 잘 모르시는 분에게 도움 될까 하고 이렇게 한번 강좌 아닌 강좌를 올립니다.
StressTest.zip
안내 1 : IT 관련 개인메타블로그 개설 하였습니다. 많은 동참 바랍니다.
안내 2 :
안내 3 :
혹시 통신과 파일 전송에 대해 궁금하신분이 있을것 같아 한번 강좌식으로 한번 포스팅 해봅니다.(반응이 좋으면 다른 테스트 프로그램을 만들시 계속적으로 포스팅 해보겠습니다.)
일단 작업 환경은 다음과 같습니다.
OS : Windows XP SP2
Tools : VC 6.0 SP4
UI는 다이얼로그 베이스로 몇가지 입력 받아 처리 할 수 있게 만들어 놓았습니다.
UI설명은 일단 하지 않겠습니다.
먼저 프로그램 실행 시 다이얼로그 베이스이기에 OnInitDialog()가 호출 될 것입니다.
호출 시 여러 쓰레드에서 동시 전송으로 스트레스 테스트 하기 때문에 이 쓰레드를 관리 하는 리스트를 초기화합니다.
m_pThreadList = new THREAD_POOL;
m_pThreadList->next = m_pThreadList;
m_pThreadList->prev = m_pThreadList;
필요한 정보를 입력 받아 시작 버튼을 클릭 합니다. 시작버튼이 눌러 지면 지정된 스레드 갯수 만큼 생성 시키고 생성된 쓰레드는 쓰레드 리스트에 추가를 합니다.
void CStressTestDlg::OnStart()
{
// TODO: Add your control notification handler code here
...
for(UINT i = 0; i < m_uThreadCount; i++)
{
THREAD_POOL *pThread = new THREAD_POOL;
pThread->nThreadnum = i + 1;
pThread->pThread = AfxBeginThread(threadSendData, this);
//쓰레드 리스트에 신규 쓰레드 추가
pThread->next = m_pThreadList->next;
pThread->prev = m_pThreadList;
m_pThreadList->next->prev = pThread;
m_pThreadList->next = pThread;
}
// 0.5초마다 쓰레드 체크
SetTimer(1, 500, NULL);
}
위 코드를 보면 알겠지만 타이머로 0.5 간격으로 쓰레드가 종료 되었는지 확인을 합니다. 타이머 코드를 보겠습니다.
if(nIDEvent == 1)
{
KillTimer(1);
// 쓰레드가 존재 하지 않으면 타이머 종료 하고 모든 프로세스 종료.
if(m_pThreadList->next == m_pThreadList)
{
m_btnStart.EnableWindow(TRUE);
m_btnCancel.EnableWindow(TRUE);
}
else
{
// 쓰레드가 종료 되면 리스트에서 삭제 하고 메모리 해제 한다.
THREAD_POOL *pInfo, *pNext;
for(pInfo = m_pThreadList->next; pInfo != m_pThreadList; )
{
pNext = pInfo->next;
DWORD dwCode;
CString strMsg = "";
if(GetExitCodeThread(pInfo->pThread->m_hThread, &dwCode))
{
if(dwCode != STILL_ACTIVE)
{
strMsg.Format("Thread num %d END(dwCode)", pInfo->nThreadnum);
SetViewMessage(strMsg);
pInfo->prev->next = pNext;
pNext->prev = pInfo->prev;
delete pInfo;
}
}
else
{
strMsg.Format("Thread num %d END(GetExitCodeThread)", pInfo->nThreadnum);
SetViewMessage(strMsg);
pInfo->prev->next = pNext;
pNext->prev = pInfo->prev;
delete pInfo;
}
pInfo = pNext;
}
// 0.5초마다 쓰레드 체크
SetTimer(1, 500, NULL);
}
}
위 처럼 쓰레드 종료 코드를 확인 하여 쓰레드 종료 확인을 하고 리스트에서 할당 되어 있는 메모리를 해제 해줍니다.
다이얼로그에서는 위와 같은 일만 합니다. 주 작업은 쓰레드에서 이루어집니다. 쓰레드 함수를 지금부터 보겠습니다.
// thread.cpp
UINT threadSendData(LPVOID pParam)
{
...
// 전송 할 파일 사이즈 구하는 부분
int nFileSize = 0;
int fp = _open (strFileName, _O_RDONLY | _O_BINARY); // _O_BINARY 를 써주어야 binary mode 로 파일 open
struct _stat stStat;
_fstat (fp, &stStat);
nFileSize = stStat.st_size;
_close(fp);
//////////////////////////
// 전송 카운트만큼 전송합니다..
for(UINT i = 0; i < uCount; i++)
{
// 소켓을 생성 합니다. SOCK_STREAM으로 TCP로 생성합니다.
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in ToAddr;
memset(&ToAddr, 0, sizeof(struct sockaddr_in));
if(hSocket == SOCKET_ERROR)
{
AfxEndThread(TRUE);
return FALSE;
}
// 접속할 IP를 설정합니다.
ToAddr.sin_family = AF_INET;
ToAddr.sin_port = htons(uPort);
ToAddr.sin_addr.S_un.S_addr = inet_addr(strIP);
// connect()로 파일 서버로 전송을 합니다.
if(connect(hSocket, (struct sockaddr *)&ToAddr, sizeof(struct sockaddr)) < 0)
{
closesocket(hSocket);
AfxEndThread(TRUE);
return FALSE;
}
// 전송할 파일을 엽니다. FILE_SHARE_READ를 준것은 여러 쓰레드가 동시에 파일을 사용 하기때문에 읽기 권한을 공유 합니다.
HANDLE hFile = CreateFile(strFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
closesocket(hSocket);
AfxEndThread(TRUE);
return FALSE;
}
// 확장성을 위해 테스트하고자하는 프로그램에 따라 파일 전송전 아규먼트 전송이 있을시를 대비해 보내는 부분을 구현 해 놓았습니다.
int len;
len = send(hSocket, strArgument, strArgument.GetLength(), 0);
if(len < 0)
{
CloseHandle(hFile);
closesocket(hSocket);
AfxEndThread(TRUE);
return FALSE;
}
DWORD dwRead;
// 1024 byte단위로 읽을 버퍼
BYTE byteData[1024];
// 1024 byte단위로 읽어서 서버 쪽으로 전송을 합니다.
for(int nSize = 0; nSize < nFileSize; )
{
memset(byteData, 0, 1024);
ReadFile(hFile, byteData, 1024, &dwRead, NULL);
nSize += dwRead;
send(hSocket, (char *)byteData, dwRead, 0);
}
CloseHandle(hFile);
closesocket(hSocket);
// 점송 후 딜레이...
Sleep(uDelay);
}
// 쓰레드를 종료 합니다.
AfxEndThread(TRUE);
return TRUE;
}
주석으로 간략 하게 설명을 드렸습니다. 전 처음 통신프로그램을 만들때가 리눅스에 만들다 보니 코드를 보면 버클리 Socket을 사용하여 만들었습니다. 좀더 세밀하게 조절 할 수 있어 선호하는 방식입니다. 버클리 방식은 책에 많이 있어 간략 하게 말씀을 드리겠습니다.
socket(); // 사용할 소켓 핸들을 생성하는 함수입니다. 만약 UDP로 생성을 하고자 하신다면 SOCK_DGRAM으로 하면 됩니다.
connect() : 지정 된 IP와 포트로 접속을 하는 함수입니다. UDP인 경우 connect()함수를 사용해도 되고 안해도 됩니다.
send()/recv() : 데이터를 주고 받는 함수입니다.
closesocket() : 생성된 소켓핸들을 해제 해주는 함수입니다.
이상 간단한 파일 전송 프로그램입니다. 아직 통신프로그램에 대해 잘 모르시는 분에게 도움 될까 하고 이렇게 한번 강좌 아닌 강좌를 올립니다.
주의사항 보기
StressTest.zip파일 다운로드 받기
안내 1 : IT 관련 개인메타블로그 개설 하였습니다. 많은 동참 바랍니다.
안내 2 :
안내 3 :
트랙백 보낼 주소 : http://fisher.tistory.com/trackback/121
-
파일 전송을 위한 코드
from 盡人事待天命2007/04/14 17:59<P>파일 전송을 위한 함수가 있을 거라는 막연한 기대를 가지고 있었지만, </P> <P>다른 한쪽으로는 파일을 읽으면서 데이터를 일정한 량만큼 전송하는 것이지 않을까하는</P> <P>생각을 하기도 했었는데 그 생각이 맞는것 같군요...</P> <P> </P> <P>좋은 코드 감사합니다.</P>
이올린에 북마크하기
이올린에 추천하기




