190712-3 Overlapped 모델
동기 :
비동기 : 완료보고를 받고 동작을 처리해야한다.
닷넷에가면 비동기 델리게이트로 구현되어있다.
// 소켓 정보 저장을 위한 구조체
struct SOCKETINFO
{
WSAOVERLAPPED overlapped;
SOCKET sock;
char buf[BUFSIZE+1];
int recvbytes;
int sendbytes;
WSABUF wsabuf; // 전송할 데이터의 크기와 버퍼의 주소값이 포함되는 구조체 (WSAsend함수에필요)
};
// 더미(dummy) 이벤트 객체 생성
WSAEVENT hEvent = WSACreateEvent();
if(hEvent == WSA_INVALID_EVENT)
err_quit("WSACreateEvent()");
EventArray[nTotalSockets++] = hEvent;
int retval = WSARecv(ptr->sock, &(ptr->wsabuf), 1, &recvbytes,
&flags, &(ptr->overlapped), NULL); // 노란색 인자에 오버랩드 구조체를 지정해야 비동기 IO 작업이다.
// 비동기 입출력 결과 확인
DWORD cbTransferred, flags;
retval = WSAGetOverlappedResult(ptr->sock, &(ptr->overlapped), //!
&cbTransferred, FALSE, &flags);//cbTransferred 몇 바이트 완료했는가
워커 스레드
// 비동기 입출력 처리
DWORD WINAPI WorkerThread(LPVOID arg)
{
int retval;
while(1){
// 이벤트 객체 관찰
DWORD index = WSAWaitForMultipleEvents(nTotalSockets, //WSAWaitForMultipleEvents()
EventArray, FALSE, WSA_INFINITE, FALSE);
if(index == WSA_WAIT_FAILED){
err_display("WSAWaitForMultipleEvents()");
continue;
}
index -= WSA_WAIT_EVENT_0;
WSAResetEvent(EventArray[index]); //- 다시 리셋
if(index == 0) continue;
// 클라이언트 정보 얻기
SOCKETINFO *ptr = SocketInfoArray[index]; //-해당 구조체 주소
SOCKADDR_IN clientaddr;
int addrlen = sizeof(clientaddr);
getpeername(ptr->sock, (SOCKADDR *)&clientaddr, &addrlen);
// 비동기 입출력 결과 확인
DWORD cbTransferred, flags;
retval = WSAGetOverlappedResult(ptr->sock, &(ptr->overlapped), //!
&cbTransferred, FALSE, &flags);//cbTransferred 몇 바이트 완료했는가
if(retval == FALSE || cbTransferred == 0){
if(retval == FALSE)
err_display("WSAGetOverlappedResult()");
RemoveSocketInfo(index);
printf("[TCP 서버] 클라이언트 종료: IP 주소=%s, 포트 번호=%d\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
continue;
}
// 데이터 전송량 갱신
if(ptr->recvbytes == 0){
ptr->recvbytes = cbTransferred;
ptr->sendbytes = 0;
// 받은 데이터 출력
ptr->buf[ptr->recvbytes] = '\0';
printf("[TCP/%s:%d] %s\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), ptr->buf);
}
else{
ptr->sendbytes += cbTransferred;
}
if(ptr->recvbytes > ptr->sendbytes){
// 데이터 보내기
ZeroMemory(&(ptr->overlapped), sizeof(ptr->overlapped)); //- 구조체 초기화 -> send 전용 구조체로 탈바꿈
ptr->overlapped.hEvent = EventArray[index];
ptr->wsabuf.buf = ptr->buf + ptr->sendbytes; //- 보내고자하는 시작주소
ptr->wsabuf.len = ptr->recvbytes - ptr->sendbytes; //- 보낼 바이트 수
DWORD sendbytes;
retval = WSASend(ptr->sock, &(ptr->wsabuf), 1, &sendbytes, //- WSASend() : 내부적으로 비동기적으로
0, &(ptr->overlapped), NULL);
if(retval == SOCKET_ERROR){
if(WSAGetLastError() != WSA_IO_PENDING){
err_display("WSASend()");
}
continue;
}
}
else{ //- 다 보냈을 경우
ptr->recvbytes = 0;
// 데이터 받기
ZeroMemory(&(ptr->overlapped), sizeof(ptr->overlapped));//- 구조체 초기화 -> recv 전용 구조체로 탈바꿈
ptr->overlapped.hEvent = EventArray[index];
ptr->wsabuf.buf = ptr->buf;
ptr->wsabuf.len = BUFSIZE;
DWORD recvbytes;
flags = 0;
retval = WSARecv(ptr->sock, &(ptr->wsabuf), 1, &recvbytes,//- WSARecv() : 비동기 입출력 시작
&flags, &(ptr->overlapped), NULL);
if(retval == SOCKET_ERROR){
if(WSAGetLastError() != WSA_IO_PENDING){
err_display("WSARecv()");
}
continue;
}
}
}
}
TCPServerClient09_Overlapped 모델 CompletionRoutine 사용
CompletionRoutine : 비동기 입출력이 완료되면 이 함수가 자동으로 호출된다.
while(1){
while(1){
// alertable wait
DWORD result = WaitForSingleObjectEx(hEvent, INFINITE, TRUE);//-bAlertable 상태를 True로 만든다.
if(result == WAIT_OBJECT_0) break; // 클라이언트가 새로 들어왔을 경우
if(result != WAIT_IO_COMPLETION) return -1;
}
bAlertable
'Socket Programming' 카테고리의 다른 글
IOCP Model (0) | 2019.09.22 |
---|---|
WSAEventSelect Model - 2 (0) | 2019.09.20 |
WSAAsyncSelect Model (0) | 2019.09.19 |
[Socket] SELECT MODEL 네트워크 (0) | 2019.09.18 |
비동기 IO (Overlapped IO) (0) | 2019.09.18 |