본문 바로가기

기술자료/CPP

[강좌] 파일전송 전용 확장 DLL 을 만들자! -> 두번째 이야기

안녕하십니까?

에휴 어저께 친구 어머님이 돌아가셔서 밤을 샜더니 아직도
피곤이 남아있네요 ㅡ_ㅡ;

지난번의 이어서 계속 하겠습니다.

지난번에 초기화에 대해서 했는데, 이번에는 실제로 클라이언트에서
접속이 이루어졌을때 스레드를 생성하고 Accept 하는 과정까지 하겠습니다.
(생각같아서는 다 해버리고 싶은데 넘 양이 많고 힘들답니다 이해바람 ^^)

이번과정도 일반 소켓 프로그래밍 하실때 하는 알고리즘이기 때문에
아마 생소하지 않을 것입니다.

클라이언트로부터 접속 요청이 왔을때 스레드를 생성하고 새로운
통신용 소켓을 만드는 부분의 소스를 보시겠습니다.

// Listen.cpp : implementation file
//

#include "stdafx.h"
#include "ClientThread.h" // 이 헤더파일을 추가합니다.
#include "Listen.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CListen

CListen::CListen()
{
}

CListen::~CListen()
{
}


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CListen, CAsyncSocket)
    //{{AFX_MSG_MAP(CListen)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif    // 0

/////////////////////////////////////////////////////////////////////////////
// CListen member functions

void CListen::OnAccept(int nErrorCode)
{
    // TODO: Add your specialized code here and/or call the base class
    CSocket sock;
    Accept(sock);
    CClientThread* pThread = (CClientThread*)AfxBeginThread(RUNTIME_CLASS(CClientThread),
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL); // 스레드를 만들고 대기시킵니다.
    SOCKET socket = sock.Detach(); // 소켓핸들을 분리해서
    pThread->sSock = socket; // 생성된 스레드로 넘깁니다.
    pThread->ResumeThread(); // 스레드를 계속 돌립니다.
    CAsyncSocket::OnAccept(nErrorCode);
}

대충 보셔도 아시겠죠? ^^

접속 요청이 오면 우리가 만들어놓은 스레드 클래스를 이용해서 새로운 스레드를
만듭니다. 그후 Accept 된 소켓을 이 스레드에 접목시키면 되는것이죠
단. 주의하실 사항은 소켓은 생성된 스레드 이외의 다른 스레드에서 사용을 할수가
없답니다. 그래서 소켓 핸들을 분리해서(Detach) 사용하고자 하는 다른 스레드에서
연결(Attach)한후 사용할수 있답니다.

그럼 스레드에서 소켓 연결하는 부분을 보시겠습니다.

/////////////////////////////////////////////////////////////////////////////
// CClientThread thread

#include "Active.h"

class CClientThread : public CWinThread
{
    DECLARE_DYNCREATE(CClientThread)
protected:
    CClientThread();           // protected constructor used by dynamic creation

// Attributes
public:
    SOCKET sSock; // 받아올 소켓 핸들
    CActive sock; // 통신용 소켓 클래스
// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CClientThread)
    public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
    //}}AFX_VIRTUAL

// Implementation
protected:
    virtual ~CClientThread();

    // Generated message map functions
    //{{AFX_MSG(CClientThread)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

위의 소스는 스레드의 헤더 파일입니다.
Cpp 파일은 다음과 같습니다.

// ClientThread.cpp : implementation file
//

#include "stdafx.h"
#include "ClientThread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CClientThread

IMPLEMENT_DYNCREATE(CClientThread, CWinThread)

CClientThread::CClientThread()
{
}

CClientThread::~CClientThread()
{
}

BOOL CClientThread::InitInstance() // 이부분은 아까 Accept 부분에서 ResumeThread()를 호출하면 발생합
니다.
{
    // TODO:  perform and per-thread initialization here
    sock.Attach(sSock); // 받아온 소켓 핸들을 CActive 클래스에 붙입니다.
    sock.m_pThread = this; // 소켓에서 자신의 스레드가 어떤것인지를 알려줍니다.

    return TRUE;
}

int CClientThread::ExitInstance()
{
    // TODO:  perform any per-thread cleanup here
    return CWinThread::ExitInstance();
}

BEGIN_MESSAGE_MAP(CClientThread, CWinThread)
    //{{AFX_MSG_MAP(CClientThread)
        // NOTE - the ClassWizard will add and remove mapping macros here.
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CClientThread message handlers

다음은 통신용 소켓(CActive)의 소스를 잠깐 보시죠 ^^

/////////////////////////////////////////////////////////////////////////////
// CActive command target

class CActive : public CSocket
{
// Attributes
public:

// Operations
public:
    CActive();
    virtual ~CActive();
public:
    CWinThread* m_pThread; // 자신의 소켓을 돌리고 있는 스레드 포인터

// Overrides
public:
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CActive)
    public:
    virtual void OnReceive(int nErrorCode);
    virtual void OnClose(int nErrorCode);
    //}}AFX_VIRTUAL

    // Generated message map functions
    //{{AFX_MSG(CActive)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG

// Implementation
protected:
};

// Active.cpp : implementation file
//

#include "stdafx.h"
#include "ClientThread.h" // 이 헤더파일을 추가
#include "Active.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CActive

CActive::CActive()
{
}

CActive::~CActive()
{
}


// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CActive, CSocket)
    //{{AFX_MSG_MAP(CActive)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif    // 0

/////////////////////////////////////////////////////////////////////////////
// CActive member functions


void CActive::OnClose(int nErrorCode) // 접속이 종료될때
{
    // TODO: Add your specialized code here and/or call the base class
    Close(); // 종료하고 ㅡㅡ;
    m_pThread->PostThreadMessage(WM_QUIT, 0, 0); // 이 소켓을 돌리는 스레드도 종료합니다.
    m_pThread = NULL;
    CSocket::OnClose(nErrorCode);
}

소스가 한곳에 모여있는 게 아니라서 약간 헥갈릴수가 있으니 눈 크게 뜨시고
소스를 보세요 ^^

궁금하신 사항은 리플 달아주세요

p.s. 강좌는 계속 됩니다~ 쭈욱~