위즈네트 아카데미

TUTORIAL

튜토리얼

Home  > 튜토리얼

W5500 ioLibrary 100% 활용하기 (3)

재원 하 2015.06.08 17:51 조회 수 : 47




앞서 살펴 본 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 rn", ret);
close(sn);
break;
}
ret = connect(sn,ip,5000);
if(ret < 0)
{
printf("ERROR : SOCKET Connect , ErrCode=%d rn", ret);
close(sn);
break;
}
while(1)
{
ret = recv(sn, buf, sizeof(buf));
if(ret < 0)
{
printf("ERROR : SOCKET recv , ErrCode=%d rn", ret);
disconnect(sn);
close(sn);
break;
}
ret = send(sn, buf, ret);
if(ret < 0)
{
printf("ERROR : SOCKET send , ErrCode=%d rn", 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!!!";
while(1)
{
ret = socket(sn, Sn_MR_TCP, 5000, 0x00);
if(ret < 0)
{
printf("ERROR : SOCKET open , ErrCode=%d rn", ret);
close(sn);
break;
}
ret = listen(sn);
if(ret < 0)
{
printf("ERROR : SOCKET Listen , ErrCode=%d rn", 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 rn", ret);
close(sn);
break;
}
while(1)
{
ret = send(sn, buf, strlen(buf));
if(ret < 0)
{
printf("ERROR : SOCKET send , ErrCode=%d rn", ret);
close(sn);
break;
}
ret = recv(sn, buf, sizeof(buf));
if(ret < 0)
{
printf("ERROR : SOCKET recv , ErrCode=%d rn", 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 rn", 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 rn", 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 rn", 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 rn", 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 &lt; 0)
{
printf("ERROR : SOCKET open , ErrCode=%d rn", ret);
close(sn);
break;
}
ret = connect(sn,ip,5000);
if(ret &lt; 0)
{
printf("ERROR : SOCKET Connect , ErrCode=%d rn", ret);
close(sn);
break;
}
}
else if(getSn_SR(sn) == SOCK_ESTABLISHED) // SOCKET 이 접속에 성공했다면,
{
ret = send(sn, buf, ret);
if(ret &lt; 0)
{
printf("ERROR : SOCKET send , ErrCode=%d rn", ret);
disconnect(sn);
close(sn);
break;
}
else if(ret &gt; 0) // 수신 DATA가 있다면 Loopback 합니다.
{
while(1) // SOCK_BUSY 인 경우 재시도를 위해 while() loop 사용
{
ret = recv(sn, buf, sizeof(buf)); // SOCK_BUSY인 경우 반복 호출합니다.
if(ret &lt; 0)
{
printf("ERROR : SOCKET recv, ErrCode=%d rn", 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() &gt;0) recv();
if(getSn_TX_FSR() &gt; 0) send();
break;
case SOCK_CLOSE_WAIT: // 이 상태는 Half-closed 상태로 DATA를 송수신할 수 있습니다.
if((len=getSn_RX_RSR()) &gt;0) recv(len);
if((len=getSn_TX_FSR()) &gt; 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()) &gt; 0) recvfrom();
if((len=getSn_TX_RSR()) &gt; 0) sendto();
break;
case SOCK_CLOSED:
socket();
break;
}
}

Example by using the skeleton code


ioLibraray 의 Loopback Application을 참조하시기 바랍니다.

[Loopbac Example in ioLibrary]


 

ioLibrary 100% 활용하기 – 1

ioLibrary 100% 활용하기 – 2

ioLibrary 100% 활용하기 - 3

ioLibrary 100% 활용하기 - 4

출처 : 밤소의 잠못드는 밤 IoT 이야기