*스크립트도 패킷에 함께 날라온다.!
*패킷구조체 (file송/수신용)
typedef struct _packet{
WORD MajorVer, MinVer; //버전을 담아두면 하위호환시에 유리하다.
char szCommand[16]; //명령용(int size두면 좋음.. 패킷의크기를알도록)
int sizefile; //수신파일사이즈
char file; //파일임
}
*서버짤때 고려할 사항 안정성 > 성능향상 > 서비스의 질
*파일 송수신시 ::TransmitFile()메소드 사용하자. 다해준다. ㅋㅋㅋ
*IOCP는 처음에 등록하기가 어렵지만, 한번 등록해두고나면 무진장 편하다.
*TCP Echo Server잘 보자. ㅋ
*IOCP에서 중요한것을 스레드가 할일을 잘게 쪼개라 그렇지않다면 문제생길수 있다.
*IOCP서버만들기.
//ClientData class
typedef struct _IO_DATA{
WSAOVERLAPPED OverLapped;
BYTE byBuffer[150000]; //송수신 저장버퍼
WSABUF WsaBuffer;
LPVOID CClientData //클라이언트정보
} IO_DATA;
class CClientData{
Public:
생성자(); 소멸자();
public:
IO_DATA m_IODATA;
Socket m_hSocket;
SOCK_ADDR m_Address;
}
//서버 리슨부분
HANDLE hIOCPHancle = ::CreateIOCompletionPort(FileHandle(INVALID_HANDLE_VALUE),NULL,0,0); //소켓에엮기위해
if( hIOCPHandle == NULL){ To do Error handling; }
loop(i to n){
AfxBeginThread(CompletionThread(함수명),(LPVOID)hIOCPHandle(IOCP핸들)); //이거는알아서호출되는함수다.
}
g_hSockServer = ::WSASocket(PF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
//window에서 사용될 winsock2함수이다.
if( g_hSockServer == NULL){ To do Error; }
SOCKADDR 처리
그외에는 일반서버와 동일
//bind
//listen
//accept후
while(TRUE){
hClientSocket = ::accept(g_hSockServer,(SOCKADDR*)&CliAddr,&nAddrSize);
if(hClientSocket == INVALID_SOCKET){ break;}
CClientData pNewClientData = new CClientData;
소켓정보를 저장(ClientData에)
::CreateIOCompletionPort((HANDLE)pNewClientData->m_hSocket,
hIOCPHandle, (DWORD)&pNewClientdata->m_IOData, 0);
g_ptrList.AddTail();
dwRecvSize= 0;
::WSARecv(pNewClientData->m_hSocket,
&pNewClientData->m_IOData.WsaBuffer,1,&dwRecvSize,&dwFlag,
&pNewClientData->m_IOData.Overlapped,NULL); //큐에 걸어놓는작업임
if(WSAGetLastError() != WSA_IO_PENDING){ to do error; }
}
CloseHandle(hHandle);
/////////////////////////////////////////////////////////////////////////////
넘어온함수(스레드함수)
while(TRUE){
::GetQueuedCompletionStatus(hIOCPHandle, &dwTransize,&IODATA,,INFINITE);
//todo something
::WSASend(...)
!주목할것
(DWORD)&pClientData->m_IODATA == (DWORD)pIOData) //수신한 곳에서생성한것과 큐에서꺼낸것의주소가같다. 비동기IO와 관련해서 데이터찾아갈 수 있다.
(DWORD)&pClientData->m_IOData.byBuffer == (DWORD)pClientData->m_IOData.WsaBuffer.buf
//데이터를 빼야하는 이유. 접속한 사용자별로 버퍼를 따로 두어야한다.
BufferCLEAR
::WSARecv를 이용다시 슬립상태로 들어간다.
}
}
g_hSockServer = ::WSASocket(PF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
//window에서 사용될 winsock2함수이다.
if( g_hSockServer == NULL){ To do Error; }
SOCKADDR 처리
그외에는 일반서버와 동일
//bind
//listen
//accept후
while(TRUE){
hClientSocket = ::accept(g_hSockServer,(SOCKADDR*)&CliAddr,&nAddrSize);
if(hClientSocket == INVALID_SOCKET){ break;}
CClientData pNewClientData = new CClientData;
소켓정보를 저장(ClientData에)
::CreateIOCompletionPort((HANDLE)pNewClientData->m_hSocket,
hIOCPHandle, (DWORD)&pNewClientdata->m_IOData, 0);
g_ptrList.AddTail();
dwRecvSize= 0;
::WSARecv(pNewClientData->m_hSocket,
&pNewClientData->m_IOData.WsaBuffer,1,&dwRecvSize,&dwFlag,
&pNewClientData->m_IOData.Overlapped,NULL); //큐에 걸어놓는작업임
if(WSAGetLastError() != WSA_IO_PENDING){ to do error; }
}
CloseHandle(hHandle);
/////////////////////////////////////////////////////////////////////////////
넘어온함수(스레드함수)
while(TRUE){
::GetQueuedCompletionStatus(hIOCPHandle, &dwTransize,&IODATA,,INFINITE);
//todo something
::WSASend(...)
!주목할것
(DWORD)&pClientData->m_IODATA == (DWORD)pIOData) //수신한 곳에서생성한것과 큐에서꺼낸것의주소가같다. 비동기IO와 관련해서 데이터찾아갈 수 있다.
(DWORD)&pClientData->m_IOData.byBuffer == (DWORD)pClientData->m_IOData.WsaBuffer.buf
//데이터를 빼야하는 이유. 접속한 사용자별로 버퍼를 따로 두어야한다.
BufferCLEAR
::WSARecv를 이용다시 슬립상태로 들어간다.
}
반응형
댓글