티스토리 뷰

  여태껏 만들던 서버는 동기 형식의 서버로써, accept() | connect() | send() | recv() | sendto() | recvfrom() 함수 모두 호출 시 조건이 만족될 때 까지 무한정 대기하는 형식을 취합니다. 따라서 조건이 맞지 않으면 다른 행동을 할 수 없습니다. 

 

  그렇다면, 비동기 형식은 무엇이냐? 비동기 형식은 소켓 함수 호출 시 조건이 만족되지 않더라도 함수가 리턴을 하므로 중단 없이 다음 코드를 수행할 수 있습니다. 다만 조건이 만족되지 않아서 리턴하게 되면 WSAEWOULDBLOCK코드를 리턴합니다. 따라서 WSAGetLastError()를 통해서 확인을 해야 합니다.

 

호출 방식은 아래와 같습니다. 전체적인 예제도 참고해서 봐주시면 감사하겠습니다. Client는 기본 세팅으로도 가능하기에 따로 올리지는 않겠습니다.

//소켓 생성
    listen_s = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_s == INVALID_SOCKET)
        err_quit("socket");

    //비동기 소켓으로 전환 하는 과정
    u_long on = 1;
    retVal = ioctlsocket(listen_s, FIONBIO, &on);
    if (retVal == SOCKET_ERROR) err_quit("ioctlsocket()");

//DummyServer

#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
#include<stdio.h>
#include<Winsock2.h>
#include<stdlib.h>

#define PORT	9000
#define BUFSIZE 512

void err_quit(const char* msg) {
    LPVOID lpMsgBuf;
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, WSAGetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);
    printf("[%s] %s\n", msg, (char*)lpMsgBuf);
    LocalFree(lpMsgBuf);
    exit(1);
}

int main()
{
    WSADATA wsaData;
    SOCKET listen_s, clientsock;
    SOCKADDR_IN sock_addr, clientaddr;
    char buf[BUFSIZE + 1];
    int addrlen, retVal;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        err_quit("WSAStartup");

    //소켓 생성
    listen_s = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_s == INVALID_SOCKET)
        err_quit("socket");

    //비동기 소켓으로 전환 하는 과정
    u_long on = 1;
    retVal = ioctlsocket(listen_s, FIONBIO, &on);
    if (retVal == SOCKET_ERROR) err_quit("ioctlsocket()");

    ZeroMemory(&sock_addr, sizeof(struct sockaddr_in));
    sock_addr.sin_family = PF_INET;
    sock_addr.sin_port = htons(PORT);
    sock_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

    if (bind(listen_s, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR)
    {
        err_quit("Bind");
        return 1;
    }
    if (listen(listen_s, 10) == SOCKET_ERROR)
    {
        err_quit("listen");
        return 1;
    }

    while (1) {
        //
    ACCEPT_AGAIN:
        addrlen = sizeof(clientaddr);
        clientsock = ::accept(listen_s, (struct sockaddr*)&clientaddr, &addrlen);
        if (clientsock == INVALID_SOCKET) {
            if (WSAGetLastError() == WSAEWOULDBLOCK)
                goto ACCEPT_AGAIN;
            err_quit("accept()");
            break;
        }

        //접속한 클라리언트 정보 출력
        printf("[TCP서버] 클라이언트 접속 IP : %s %d\r\n",
            inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

        //클라이언트와 데이터 통신
        while (1) {
        RECEVICE_AGAIN:
            retVal = recv(clientsock, buf, BUFSIZE, 0);
            if (retVal == SOCKET_ERROR) {
                if (WSAGetLastError() == WSAEWOULDBLOCK)
                    goto RECEVICE_AGAIN;
                err_quit("recv()");
                break;
            }
            else if (retVal == 1)
                break;

            //받은 데이터 출력
            buf[retVal] = '\0';
            printf("[TCP/%s : %d] %s\r\n", inet_ntoa(clientaddr.sin_addr),
                ntohs(clientaddr.sin_port), buf);

            //데이터 보내기
        SEND_AGAIN:
            retVal = send(clientsock, buf, retVal, 0);
            if (retVal == SOCKET_ERROR) {
                if (WSAGetLastError() == WSAEWOULDBLOCK)
                    goto SEND_AGAIN;
                err_quit("send()");
                break;
            }
        }
        closesocket(clientsock);
        printf("[TCP 서버] 클라이언트 종료 : IP  주소 = %s, 포트번호 = %d\r\n",
            inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
    }

    closesocket(listen_s);
    WSACleanup();
    return 0;
}
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함