티스토리 뷰

Socket Programming

Overlapped Model struct

열혈허슬러 상추님 2019.09.21 16:37
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
Overlapped Model struct  (0) 2019.09.21
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
댓글
댓글쓰기 폼