MFC/Serial & Socket

Thread를 사용해서 Socket관리하기

와앙노 2010. 11. 29. 10:43

소켓통신을 할 때 짧은 메시지는 빠르게 Send/Receive하기 때문에 못 느끼지만 용량이 큰 파일을 보내거나 받을 때 Thread를 사용하지 않으면 프로그램이 응답 없음 상태로 되어 버린다.

그래서 파일전송을 할 때에는 서버에서는 접속하는 Socket이 생길 때 마다 Socket을 생성하고 Thread를 생성하여 Thread안에 소켓을 붙여서 관리하면 프로그램이 응답 없음 상태가 되는 것을 막을 수 있다.

 

소켓을 Thread에 붙이려면 두 가지 함수가 필요하다

BOOL CAsyncSocket::Attach(SOCKET hSocket, long lEvent)

{

           ASSERT(hSocket != INVALID_SOCKET);

 

           m_hSocket = hSocket;

           CAsyncSocket::AttachHandle(hSocket, this);

 

           return AsyncSelect(lEvent);

}

SOCKET CAsyncSocket::Detach()

{

           SOCKET hSocket = m_hSocket;

           if (AsyncSelect(0))

           {

                     CAsyncSocket::KillSocket(hSocket, this);

                     m_hSocket = INVALID_SOCKET;

                     return hSocket;

           }

           return INVALID_SOCKET;

}

Detach()함수는 해당 소켓을 분리하는 것이고 Attach()함수는 인자로 받은 소켓을 자신이 갖고있는 소켓변수에 붙이는 역할을 한다.

void CServSocket::OnAccept(int nErrorCode)

{

           CIntSocket sock;

          

           Accept(sock);

           CSockThread* pThread =

 (CSockThread*)AfxBeginThread

(RUNTIME_CLASS(CSockThread),THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);

           if(!pThread)

           {

                     sock.Close();

                     TRACE("Thread could not be Created\n");

                     return;

           }

 

           //thread에 변수 초기화

           CDENT_MSG_SERVERDlg* pDlg = (CDENT_MSG_SERVERDlg*)m_pWnd;

           pThread->m_CIntSocket.SetDlg(pDlg);

           pThread->m_CIntSocket.m_CriticalSection = &pDlg->m_CriticalSection;

           pThread->m_CIntSocket.m_FilePath = &pDlg->m_DlgFilePath;

           pThread->m_CIntSocket.m_pThread = (CWinThread*)pThread;

 

           //접속자 추가

           CString ipnum = sock.GetUserIP();

           pDlg->m_memberL.AddString(ipnum);

           pDlg->UpdateData(TRUE);

//        pThread->m_CIntSocket.SEND_LOING_MSG_ALL(ipnum,pDlg);

           SendMsg* m = new SendMsg(0x01,ipnum,ipnum.GetLength());

           pDlg->SendMessage(SEND_ALL_USER,(WPARAM)m,0);

          

 

           pThread->m_hSocket = sock.Detach();//스레드에 소켓을 분리하여 소켓의 핸들을 넘겨준다

           pDlg->m_Socklist.AddTail(pThread);

           sock.m_pThread = pThread;

 

 

           pThread->ResumeThread();

           pDlg->m_Accetpcnt = pDlg->m_Socklist.GetCount();

           pDlg->UpdateData(FALSE);

           //접속자리스트에 방금 접속한 현재 리스트를 전송한다

           if(pDlg->m_Accetpcnt > 0)

           {         

                     pThread->m_CIntSocket.Send_LoginList(pThread);

           }

           CString _msg;

           _msg.Format("<<SYSTEM MESSAGE>> [%s] LOGIN",ipnum);

           pDlg->m_log.AddString(_msg);

           CSocket::OnAccept(nErrorCode);

}

 

메신저에서 Accept가 되었을 때 쓰레드를 생성하고 여러 개의 Socket을 관리하기 위하여 리스트에 스레드를 저장한다.

 

CSockThread* pThread = (CSockThread*)AfxBeginThread

(RUNTIME_CLASS(CSockThread),THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);

CREATE_SUSPENDED 스레드를 생성하고 우선 대기상태로 생성하는 인자이며

pThread->ResumeThread(); 호출하는 순간 Thread가 실행되며 ThreadInitInstance()함수가 실행된다.

pThread->m_hSocket = sock.Detach(); 부분은 Accept시칸 Socket을 분리하여 Thread의 소켓변수에 넣는다. 아직은 Thread에 소켓을 Attach 한 것이 아니기 때문에 완전하게 분리해서 붙인 것은 아니다

 

BOOL CSockThread::InitInstance()

{

           m_CIntSocket.Attach(m_hSocket);

           return TRUE;

}

pThread->ResumeThread(); 를 호출하면 실행되는 Thread의 함수이다

Detach하여 받은 Socket변수를 Attach하는 부분이다


소켓을 Thread에 붙이는 순서로 서버 기준이며 클라이언트에서는 소켓이 Connect되면 이와 같은 방법으로 Thread에 소켓을 붙이게 된다