ioLibrary 100% 활용하기 (3)


앞서 살펴 본 ioLibrary의 가장 큰 특징은 다음과 같다.

  • 모든 함수의 Success & Fail 처리 : SOCKET ERROR 시 상황 코드
  • Blocked & Non-Blocked I/O function
  • Datagram Data의 Flexible 처리

각 특징들이 어떻게 활용되는지 하나씩 알아보자


Blocked vs Non-Blocked I/O function

Blocked I/O function 이란, 함수를 호출 할 경우 해당 함수 수행이 완료되기 전까지 return하지 않는 함수이다.
예로, connect(sn, ip, port) 함수를 호출 하였다고 가정할 경우 ip와 port 정보를 갖는 상대방과 접속을 성공하거나 실패할 때 까지 connect() 계속 기다린다. 즉 리턴하지 않고 block 되어진다.
반면, Non-Blocked I/O function는 해당 함수의 수행이 완료되거나, 수행 결과를 기다리지 않고 호출과 동시에 미리 정의된 결과값을 리턴하게 된다. 따라서, 사용자는 함수수행의 결과에 대한 확인를 계속 확인하여야 한다.

Blocked IO Mode

User Application 이 순차적으로 수행될 필요가 있으며, 다른 작업 시간에 영향에 주지 않을 경우 적용될 수 있다.
Blocked IO Mode는 Program이 직관적이여서 Debugging이 용이하나, 함수를 수행하는 동안 Resources를 독점하므로 다른 작업을 수행할 수 없다.

ioLibrary에서 Blocked IO Function은 socket command를 수행하고 그 결과를 확인할 때까지 호출함수가 리턴하지 않음을 의미한다.

아래 코드는 Blocked IO mode로 socket api를 사용하여 Loopback Server 와 Client을 구현한 예이다.

 

  • Client
uint8_t sn = 0;
int32_t ret = 0;
uint8_t ip[4] = {192,168,0,100};
uint8_t buf[2048] = {0,};
while(1)
{
    ret = socket(sn, Sn_MR_TCP, 3000, 0x00);
    if(ret < 0)
    {
        printf("ERROR : SOCKET open , ErrCode=%d \r\n", ret);
        close(sn);
        break;
    }
    ret = connect(sn,ip,5000);
    if(ret < 0)
    {
        printf("ERROR : SOCKET Connect , ErrCode=%d \r\n", ret);
        close(sn);
        break;
    }
    while(1)
    {
        ret = recv(sn, buf, sizeof(buf));
        if(ret < 0)
        {
            printf("ERROR : SOCKET recv , ErrCode=%d \r\n", ret);
            disconnect(sn);
            close(sn);
            break;
        }
        ret = send(sn, buf, ret);
        if(ret < 0)
        {
            printf("ERROR : SOCKET send , ErrCode=%d \r\n", ret);
            disconnect(sn);
            close(sn);
            break;
        }
    }
}

recv()에서 Data를 수신하거나 실패할 경우에만, 다음 함수 Send()를 수행하게 된다. 각 단계 함수 호출이 실패하였을 경우 소켓을 해제하고 while(1)에 의해 소켓 생성, 연결, 전송, 수신을 계속 반복한다.

 

  • Server
uint8_t sn = 0;
int32_t ret = 0;
uint8_t buf[20] = "Hello, ioLibrary!!!\0";
while(1)
{
    ret = socket(sn, Sn_MR_TCP, 5000, 0x00);
    if(ret < 0)
    {
        printf("ERROR : SOCKET open , ErrCode=%d \r\n", ret);
        close(sn);
        break;
    }
    ret = listen(sn);
    if(ret < 0)
    {
        printf("ERROR : SOCKET Listen , ErrCode=%d \r\n", ret);
        close(sn);
        break;
    }
    while(getSn_SR(sn) != SOCK_ESTABLISHED);    // Wait until a peer connect to server
    if(ret < 0)
    {
        printf("ERROR : SOCKET Connect , ErrCode=%d \r\n", ret);
        close(sn);
        break;
    }
    while(1)
    {
        ret = send(sn, buf, strlen(buf));
        if(ret < 0)
        {
            printf("ERROR : SOCKET send , ErrCode=%d \r\n", ret);
            close(sn);
            break;
        }
        ret = recv(sn, buf, sizeof(buf));
        if(ret < 0)
        {
            printf("ERROR : SOCKET recv , ErrCode=%d \r\n", ret);
            close(sn);
            break;
           }
    }
}

send()에서 Data를 전송 완료하거나 실패한 후 recv()를 수행한다. 이때 send()가 성공했을 경우만 recv()를 수행해야 정상적인 Loopback이 된다.
각 단계 함수 호출이 실패하였을 경우 소켓을 해제하고 while(1)에 의해 소켓 생성, 연결, 전송, 수신을 계속 반복한다.

 

Non-Blocked IO mode

User Application 이 순차적으로 수행될 필요가 없거나, 다른 작업을 일정 시간 내에 수행해야 할 경우 사용된다.
Non-Blocked IO Mode는 Program이 작업 간 Schedule을 잘 고려해야 함으로 구현 상의 어려움이 있으며, Debugging이 쉽지 않다.

ioLibrary에서 Non-Blocked IO Function은 socket command만을 수행하고 바로 리턴하거나, 수행 준비가 되어 있지 않을 경우 바로 리턴한다. 이때 Return 값이 SOCK_OK 이라 하더라도 그 결과가 성공했음을 뜻하지는 않는다. 또 SOCK_BUSY인 경우 Command 수행 준비가 아직 되지 않은 상태이므로 반드시 확인하여 해당 command가 수행될 수 있도록 재시도를 하여야 한다.

아래 코드는 Non-Blocked IO mode로 socket api를 사용하여 Blocked IO mode의 Loopback Server 와 Client을 동일하게 순차적으로 수행될 수 있도록 구현한 예이다.

 

  • Client
uint8_t sn = 0;
int32_t ret = 0;
uint8_t ip[4] = {192,168,0,100};
uint8_t buf[2048] = {0,};
while(1)
{
    if(getSn_SR(sn) == SOCK_CLOSED)
    {
        ret = socket(sn, Sn_MR_TCP, 3000, SF_IO_NONBLOCK);  // Non-Blocked Mode로 Open
        if(ret < 0)
        {
            printf("ERROR : SOCKET open , ErrCode=%d \r\n", ret);
            close(sn);
            break;
        }
        while(getSn_SR(sn) != SOCK_INIT);    //SOCKET이 생성될 때까지 기다린다.
        ret = connect(sn,ip,5000);
        if(ret < 0)
        {
            printf("ERROR : SOCKET Connect , ErrCode=%d \r\n", ret);
            close(sn);
            break;
        }
    }
    else if(getSn_SR(sn) == SOCK_ESTABLISHED)  // SOCKET 이 접속에 성공했다면,
    {
        ret = recv(sn, buf, sizeof(buf));  // SOCK_BUSY인 경우 반복 호출한다.
        if(ret < 0)
        {
            printf("ERROR : SOCKET recv , ErrCode=%d \r\n", ret);
            disconnect(sn);
            close(sn);
            break;
        }
        else if(ret > 0)  //  수신 DATA가 있다면 Loopback 한다.
        {
            while(1) // SOCK_BUSY 인 경우 재시도를 위해 while() loop 사용
            {
                ret = send(sn, buf, ret);
                if(ret < 0)
                {
                    printf("ERROR : SOCKET send , ErrCode=%d \r\n", ret);
                    disconnect(sn);
                    close(sn);
                    break;
                }
           }
        }
    }
}
  • Server
uint8_t sn = 0;
int32_t ret = 0;
uint8_t ip[4] = {192,168,0,100};
uint8_t buf[2048] = {0,};
while(1)
{
    if(getSn_SR(sn) == SOCK_CLOSED)
    {
        ret = socket(sn, Sn_MR_TCP, 5000, SF_IO_NONBLOCK);  // Non-Blocked Mode로 Open
        if(ret < 0)
        {
            printf("ERROR : SOCKET open , ErrCode=%d \r\n", ret);
            close(sn);
            break;
        }
        ret = connect(sn,ip,5000);
        if(ret < 0)
        {
            printf("ERROR : SOCKET Connect , ErrCode=%d \r\n", ret);
            close(sn);
            break;
        }
    }
    else if(getSn_SR(sn) == SOCK_ESTABLISHED)  // SOCKET 이 접속에 성공했다면,
    {
        ret = send(sn, buf, ret);
        if(ret < 0)
        {
            printf("ERROR : SOCKET send , ErrCode=%d \r\n", ret);
            disconnect(sn);
            close(sn);
            break;
        }
        else if(ret > 0)  //  수신 DATA가 있다면 Loopback 한다.
        {
            while(1) // SOCK_BUSY 인 경우 재시도를 위해 while() loop 사용
            {
                ret = recv(sn, buf, sizeof(buf));  // SOCK_BUSY인 경우 반복 호출한다.
                if(ret < 0)
                {
                    printf("ERROR : SOCKET recv, ErrCode=%d \r\n", ret);
                    disconnect(sn);
                    close(sn);
                    break;
                }
           }
        }
    }
}

Blocked IO mode에서 Non-blocked IO 처럼 사용하기

앞서 살펴본 example은 Blocked와 Non-Blocked의 차이점을 잘 알 수 있다.
여기서 Loopback은 echo-back을 수행하는 것으로 수신한 데이타를 전송해야 하는 순차적 프로그램이다. 즉 순차적 기능을 수행해야 할 경우 Blocked IO mode가 구현하기 훨씬 쉽다. 하지만 복잡도는 높아지지만, 다른 작업을 수행할 수 있는 Non-Blocked IO mode 기능이 필요할 경우가 대부분이다.

이 두가지 장점을 모두 살리는 코드를 살펴보자. 이때 가장 중요한 함수 3가지가 있다.

  • getSn_SR() : SOCKET 상태를 모니터링한다
  • getSn_RX_RSR() : 수신한 Data 크기가 얼마인지 확인한다.
  • getSn_TX_FSR() : 자주 사용되지는 않지만, 전송 가능한 Data 크기가 얼마인지 확인한다.

ioLibrary는 이 3가지 함수를 이용하여 Blocked IO function을 Non-Blocked IO mode처럼 사용할 수 있는 skeleton code를 제공한다. skeleton code의 핵심은 위의 3가지 함수를 이용하여 Blocked IO function을 호출할 시점을 정확히 파악하여 해당 function을 호출함으로써, Blocked time은 최소하는데 있다.

Skeleton Code

 

  • TCP
while(1)
{
    switch(getSn_SR())
    {
        case SOCK_ESTABLISED :
            if(getSn_RX_RSR() >0) recv();
            if(getSn_TX_FSR() > 0) send();
            break;
        case SOCK_CLOSE_WAIT:    // 이 상태는 Half-closed 상태로 DATA를 송수신할 수 있다.
            if((len=getSn_RX_RSR()) >0) recv(len);
            if((len=getSn_TX_FSR()) > 0) send(len); // getSn_TX_FSR()은 잘 사용하지 않는다.
            disconnect();
            break;
        case SOCK_INIT :
            connect();  // For Client
            listen();    // For Server
            break;
        case SOCK_CLOSED:
            socket();
            break;
    }
}
  • UDP
while(1)
{
    switch(getSn_SR())
    {
        case SOCK_UDP :
            if((len=getSn_RX_RSR()) > 0) recvfrom();
            if((len=getSn_TX_RSR()) > 0) sendto();
            break;
        case SOCK_CLOSED:
            socket();
            break;
    }
}

Example by using the skeleton code

ioLibraray 의 Loopback Application을 참조하라.

[Loopbac Example in ioLibrary]


블로그 이미지

MidnightCow

위즈네트 칩(W5300, W5200, W7100, W7500) 개발자

,