<목차>

1. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(1)

2. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(2)

3. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(3)

4. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(4)

5. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(5)

6. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(6)

7. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(7)

8. W5500 ioLibrary_BSD를 MCU 8051로 포팅하기(8)

 

 

5. 포팅하기 - Loopback (Echo Server) 구현하기

 

지난 시간 W5500에 대한 Ping Test까지 완료하였다. 이제 본격적으로 ioLibrary를 활용하여 실제 응용을 구현해보자.

 

- TCP Loopback (Echo Server) 구현하기

Loopback 은 수신한 데이타를 아무런 가공없이 그대로 되돌려주는 Echo Server 프로그램이다.

Echo Server는 TCP Server나 Client 그리고 UDP등으로 구현이 가능한다.

Echo Server에서 Protocol은 말그대로 수신한 데이타를 아무런 가공없이 그대로 수신한 상대방에게 돌려 주는 것이다. 일반적으로 Telnet, FTP, HTTP, NTP 등과 같은 Protocol은 정해진 Data를 수신하고 그것을 해석하고 그에 상응하는 Data를 가공하여 상대방에 돌려준다. 여기서는 그런 Protocol의 구현에 앞서 가장 간단한 Echo Server를 구현해 볼것이다.

 

TCP Echo Server를 구현하기 위해 필요한 함수를 먼저 살펴보자. 각 SOCKET API들은 앞서 언급한 SOCKET_APIs.chm Doxygen 문서를 참조하라. 이 모든 함수는 Blocked (Polling) 혹은 Non-Blocked Mode로 선택적으로 함수를 사용할 수 있다. 이와 관련된 Post은 다음에 하는 것으로 하고, 우선, socket() 함수 호출 시 즉 생성시 flag 설정 시 SF_IO_NONBLCOK 값을 추가해주면 된다. flag 설정시 SF_IO_NONBLOCK을 추가하지 않을 경우, 즉 Default로 설정할 경우 SF_IO_BLOCK으로 설정된다. 또한 Socket 의 IO Mode는 setsockopt() 함수를 이용하여 동작 중에도  설정이 가능하다.

/* Socket Creatatiion */
int8_t    socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag);

/* Listen to a TCP client */
int8_t    listen(uint8_t sn);

/* Connect to a server */
int8_t   connect(uint8_t sn);

/* Send data to the peer */
int32_t  send(uint8_t sn, uint8_t* buf, uint32_t len);

/* Receive data from the peer */
int32_t  recv(uint8_t sn, uint8_t* buf, uint32_t len);

/* Disconnect the socket only used in TCP */
int8_t   disconnect(uint8_t sn);

/* Close the socket without disconnect-processing */
int8_t   close(uint8_t sn);

 

WIZnet은 TCP 관련 Application 구현 시 참고 할 수 있는 Skeleton code를 아래와 같이 제공한다.

{
    ...
    switch(getSn_SR(sn)) {
    case SOCK_ESTABILSHED:         /* TCP ESTABLISHED */
        /* TCP Server Side : When acceptting from a peer to request to connection */
        /* TCP Cleint Side :  When the connection request is acceptted by the server */ 
        //
        // TODO
        //
        // Start to data communication
        send(sn, buf, len);  // or recv(sn,buf,len)
        recv(sn,buf, len);    // or send(sn,buf,len)
        break;
    case SOCK_CLOSE_WAIT:
        /* Disconnect request  : The socket don't need any more */
        disconnect(sn);      // Don't returned until the disconnect is succeeded. 
        // or close(sn);      // Without processing to disconnect to the peer
        break;
    case SOCK_CLOSED:          /* Socket Closed State */
        /* TCP Server Side */
        socket(sn, Sn_MR_TCP,server_port, 0x00);  // Block-Io
        /* TCP Client Side : any_port is random because this can't be used as same as the previous port num */
        //socket(sn, Sn_MR_TCP,any_port, SF_IO_NONBLOCK);   // Non-Block-IO
        //
        // TODO
        //
        break;
    case SOCK_INIT:         /* TCP Socket Creatation */
        /* TCP Server Side : Wait to a conenctiion request from a peer */
        listen(sn);      
        /* TCP Client Side : Reqeust to connect to the server with server_port num */
        connect(sn,server_ip_address, server_port);  // Don't return until the connection is success
        //
        // TODO
        //
        break;
    case SOCK_LISTEN:        /* TCP Server Mode : This state can be omitted*/
        //
        // TODO
        //
        break;
    }
    ...
}

TCP Sever Skeleton 코드를 참고하여 아래와 같이 구현하여 보자.

///////////////////////////////////////////////////////////////
// Loopback Test Example Code using ioLibrary_BSD			 //
///////////////////////////////////////////////////////////////
int32_t loopback_tcps(uint8_t sn, uint8_t* buf, uint16_t port)
{
   int32_t ret;
   uint16_t size = 0, sentsize=0;
   switch(getSn_SR(sn))
   {
      case SOCK_ESTABLISHED :
         if(getSn_IR(sn) & Sn_IR_CON)
         {
            printf("%bu:Connected\r\n",sn);
            setSn_IR(sn,Sn_IR_CON);
         }
         if((size = getSn_RX_RSR(sn)) > 0)
         {
            if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
            ret = recv(sn,buf,size);
            if(ret <= 0) return ret;
            sentsize = 0;
            while(size != sentsize)
            {
               ret = send(sn,buf+sentsize,size-sentsize);
               if(ret < 0)
               {
                  close(sn);
                  return ret;
               }
               sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
         }
         break;
      case SOCK_CLOSE_WAIT :
         printf("%bu:CloseWait\r\n",sn);
         if((ret=disconnect(sn)) != SOCK_OK) return ret;
         printf("%bu:Closed\r\n",sn);
         break;
      case SOCK_INIT :
    	  printf("%bu:Listen, port [%u]\r\n",sn, port);
         if( (ret = listen(sn)) != SOCK_OK) return ret;
         break;
      case SOCK_CLOSED:
         printf("%bu:LBTStart\r\n",sn);
         if((ret=socket(sn,Sn_MR_TCP,port,0x00)) != sn)
            return ret;
         printf("%bu:Opened\r\n",sn);
         break;
      default:
         break;
   }
   return 1;
}

다소 복잡해 보이지만 getSn_RX_RSR() 함수의 사용 그리고 Return에 따른 에러 처리를 무시하고 코드를 살펴보면 Skeleton Code를 그대로 볼 수 있다. getSn_RX_RSR()함수 사용은 현재 Non-Block IO mode로 동작하는 recv() 함수가 Blocked 되는 것을 방지하기 위해 미리 수신한 크기를 확인하고 확인 후 recv() 함수를 호출하도록 구현되었다.

 

 

- UDP Loopback (Echo Server) 구현하기

UDP Echo Server를 구현하기 위해 필요한 함수를 먼저 살펴보자. SOCKET_API.chm 참조. TCP와 마찬가지로 Blocked 혹은 Non-blocked로 사용 가능하다.

/* Socket Creatatiion */
int8_t    socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag);

/* Send data to a peer */
int32_t  sendto(uint8_t sn, uint8_t* buf, uint32_t len, uint8_t* addr, uint16_t port);

/* Receive data from the peer */
int32_t  recvfrom(uint8_t sn, uint8_t* buf, uint32_t len, uint8_t* addr, uint16* port);

/* Close the socket without disconnect-processing */
int8_t   close(uint8_t sn);

 

다음은 UDP 관련 Application 구현 시 참고할 수 있는 아주 간단한 Skeleton Code를 아래와 같이 제공한다.

{
    ...
    switch(getSn_SR(sn)) {
    case SOCK_UDP:         /* TCP Socket Creatation */
        //
        // TODO
        //
        // Start to data communication
        sendto(sn, buf, len, peer_ip_address, peer_port_num);     // or recvfrom(...);
        recvfrom(sn,buf, len, peer_ip_address, peer_port_num);    // or sendto(...);
        //
        // If the socket don't need any more
        if(is_closed) close(sn);
        //
        break;
    case SOCK_CLOSED:          /* Socket Closed State */
        /* TCP Server Side */
        socket(sn, Sn_MR_UDP,server_port, 0x00);  // Block-Io
        /* TCP Client Side : any_port is random because this can't be used as same as the previous port num */
        //socket(sn, Sn_MR_TCP,any_port, SF_IO_NONBLOCK);   // Non-Block-IO
        //
        // TODO
        //
        break;
    }
    ...
}

UDP Skeleton Code를 참고하여 UDP Loopback을 아래와 같이 구현해 보자.

int32_t loopback_udps(uint8_t sn, uint8_t* buf, uint16_t port)
{
   int32_t  ret;
   uint16_t size, sentsize;
   uint8_t  destip[4];
   uint16_t destport;
   //uint8_t  packinfo = 0;
   switch(getSn_SR(sn))
   {
      case SOCK_UDP :
         if((size = getSn_RX_RSR(sn)) > 0)
         {
            if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
            ret = recvfrom(sn,buf,size,destip,(uint16_t*)&destport);
            if(ret <= 0)
            {
               printf("%bu: recvfrom error. %ld\r\n",sn,ret);
               return ret;
            }
            size = (uint16_t) ret;
            sentsize = 0;
            while(sentsize != size)
            {
               ret = sendto(sn,buf+sentsize,size-sentsize,destip,destport);
               if(ret < 0)
               {
                  printf("%bu: sendto error. %ld\r\n",sn,ret);
                  return ret;
               }
               sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
         }
         break;
      case SOCK_CLOSED:
         printf("%bu:LBUStart\r\n",sn);
         if((ret=socket(sn,Sn_MR_UDP,port,0x00)) != sn)
            return ret;
         printf("%bu:Opened, port [%u]\r\n",sn, port);
         break;
      default :
         break;
   }
   return 1;
}

 

- Loopback (Echo Server) Test 하기

앞서 구현된 loopback_tcps() loopback_udp() 함수를 ioLibrary_W5500_main.c 의 함수 선언부에 추가하고 Loopback을 위한 Data buffer를 변수 선언부에 추가한다. Data buffer 크기는 Target System에 맞게 적절히 조절한다.

/////////////////////////////////////////
// SOCKET NUMBER DEFINION for Examples //
/////////////////////////////////////////
#define SOCK_TCPS        0
#define SOCK_UDPS        1

////////////////////////////////////////////////
// Shared Buffer Definition for LOOPBACK TEST //
////////////////////////////////////////////////
#define DATA_BUF_SIZE   2048
uint8_t gDATABUF[DATA_BUF_SIZE];

 

또한  main() 함수의 main loop에 Loopback 기능을 수행할 수 있도록 다음과 같이 추가한다.

 ...
	/* Network initialization */
	network_init();

	/*******************************/
	/* WIZnet W5500 Code Examples  */
	/* TCPS/UDPS Loopback test     */
	/*******************************/
                /* Main loop */
                while(1)
	{
    	uint32_t ret = 0;
    	/* Loopback Test */
    	// TCP server loopback test
    	if( (ret = loopback_tcps(SOCK_TCPS, gDATABUF, 5000)) < 0) {
			printf("SOCKET ERROR : %ld\r\n", ret);
		}

    	// UDP server loopback test
		if( (ret = loopback_udps(SOCK_UDPS, gDATABUF, 3000)) < 0) {
			printf("SOCKET ERROR : %ld\r\n", ret);
		}
	} // end of Main loop
	// NOTREACHED

	return 0;
}

 

모든 코드 구현이 완료되었다. Test를 위해 지난 번 소개한 Hercules Program을 사용할 할 것이다. 자 컴파일 후 프로그램을 다운로드하고 다음과 같이 Target Board를 실행 해보자.

 

1. Hercules 프로그램을 Serial 모드로 실행하고 Target Board를 동작 시키면 Target Board의 출력을 다음과 같이 확인할 수 있다.

 

 

2. 또 하나의 Hercules Program을 TCP client 모드로 실행한 후, Target 보드의 Network 정보를 입력하고 접속을 시도해보자. 접속을 성공하였다면 "Hello, W5500!!!" 을 Target Board로 전송하면 Taget Board로 부터 되 돌아 오는 "Hello, W5500!!!"을 다음과 같이 확인 할 수 있다.

 

 

3. UDP Loopback을 테스트하기 위해서 또 하나의 Hercules Program을 UDP mode로 실행하고, TCP와 동일하게 Target Board의 Nework 정보를 입력한 후 [Listen] 버튼을 클린한다. UDP 채널이 성공적으로 만들어 졌다면, TCP와 동일하게 "Hello, W5500!!!"을 Target Board로 전송하고 되돌아 오는 "Hello, W5500!!!"을 다음과 같이 확인하자.

 

 

 

이상 W5500 용 ioLibrary를 C8051F380 보드로 Porting하고 간단한 TCP Loopback (Echo server) Example 구현까지 해 보았다. 글을 마치며, 좀더 자세한 설명과 사용법에 미흡한 점이 없지 않으나, 8051 사용자들에게 도움이 되길 바란다.

 

프로젝트 다운로운 

ioLibrary_W5500_For_8051.zip

 

ioLibrary는 http://wizwiki.net/ 이나 https://github.com/bingdo/ioLibrary 에서 최신 버전을 적용하기 바란다.

 

< PREV

블로그 이미지

MidnightCow

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

,