Thread를 사용해서 Socket관리하기
소켓통신을 할 때 짧은 메시지는 빠르게 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가 실행되며 Thread의 InitInstance()함수가 실행된다.
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에 소켓을 붙이게 된다