W5300 이용하여 Client 소켓 추가 방법 문의

안녕하세요.

W5300을 이용한 제작 보드로 PC와는 TCP 통신을(PC : Client, 제작 보드 : Server)
PLC와는 ModBus TCP 통신을(제작보드 : Client, PLC : Server)로 동시에 통신하려 합니다.

하나의 W5300 칩을 이용하여 어떤 상황에서는 Server로 또 어떤 상황에서는 Client로 통신하는 것이 가능한가요?
현재 PC와 W5300은 TCP 통신을 하고 있는 상황입니다.
(PC:Client, 제작 보드: Server, 사용 가능한 Socket 8개 중 7개 사용중)

또한 이를 위하여 Client 소켓을 추가해야 하는 상황입니다.
기존 메일 질문시 말씀해주신 것 처럼 Wiznet 사이트에서 프로그램 예제를 확인하였으나,
Client 소켓을 추가하기위해 구체적으로 어떤 부분을 참조해야 하는지 잘 모르겠습니다.

조금 더 상세히 설명해 주시면 감사드리겠습니다.

안녕하세요~

socket open 전에 세팅하는 것은 TCP Server 세팅하는 것과 동일합니다.
(Sn_MR의 하위 4bit를 TCP(“0001”)로 세팅)
구현의 방법에 따라 TCP client or TCP Server로 동작합니다.

TCP Server의 경우 : open → listen → recv or send → disconnect or close
TCP Client의 경우 : open → connect → recv or send ->disconnect or close

thanks, :slight_smile:

안녕하세요.

예제코드중 socket.c, h와 w5300.c h 를 참조하시면 됩니다.

W5300은 데이터시트와 doxygen 문서가 잘 되어있으니 소스를 먼저 보기보단 이 문서들을 좀 더 자세히 보시면

어렵지 않게 구현 가능하시리라 생각 됩니다. 또 한 기본적인 네트워크 지식 또한 갖추신다면, 더더욱이 쉽게 구현 하실수 있으시리라 생각 됩니다.

그리고, 한 칩에서 소켓별로 client와 server로 각각 동작이 가능하므로 어플리케이션에 따라 구현하시면 됩니다.
따로 상황이 존재하지는 않습니다.

감사합니다.

답변 감사드립니다.
제가 통신 구현 및 프로그래밍 자체적으로 초보인 단계이고 다른 코드를 수정해야 하는 상황이라 접근이 쉽지는 않네요.
기존 코드를 보니 서버로 사용 할 때 하기와 같은 핸들러를 사용합니다.
하기 handler 코드를 Server가 아닌 Client로 수정하려면 Listen 부분 대신에 Connect 부분이 들어가야 할 거 같은데 맞나요?
어떻게 수정하면 될까요??

안녕하세요.

네 맞습니다.

Server의 경우 client가 접속 하도록 listen을 하고 있지만 client의 경우 서버쪽으로 connect를 하는 것이 맞습니다

case SOCK_INIT: 
#ifdef DEBUG_MSG_W5300_API
if(s_ptr->status_prev!=s_ptr->status)
{
sprintf(gDBGstr,"Socket %u: SOCK_INIT\n", s_ptr->s);
DBG_print(gDBGstr);
}
#endif
s_ptr->status_prev=s_ptr->status;
[b]listen(s_ptr->s); [/b]
break;

위의 케이스에서 listen이 아니라 server쪽으로 connect 함수를 이용하여 connection을 맺으면 됩니다.

감사합니다.

답변 감사드립니다.
사용 가능 포트 8개중 7개를 Server로, 나머지 한개를 Client로 사용이 가능하다는 것은 알겠습니다.
그런데 Client 로 사용하는 Port를 ModBus/TCP 로 사용 가능할까요?
ModBus/TCP는 TCP 통신이 지원되면 Protocol 형식만 정해진 형식으로 전달해주면 되는 것으로 알고 있는데,
W5300 Data Sheet에는 따로 언급이 되어있지 않아 여쭤봅니다.

Modbus TCP에 대해 잘 알지 못해 간단히 검색을 해봤는데
TCP 패킷을 송수신 할 수 있다면 가능한 것으로 보입니다.

따라서 사용 가능할 것으로 보입니다.
물론 소프트웨어로 구현해야 합니다.

감사합니다.

지속적인 답변 감사드립니다.
이제 처음 시작하는 단계라 질문이 많네요.
Client 소켓 추가하여 test 중 ’ case SOCK_ESTABLISHED: ’ 상태로 접근하려면 어떻게 해야 하나요?
홈페이지 예시 프로그램에서 void loopback_tcpc(SOCKET s, uint8* addr, uint16 port, uint8* buf, uint16 mode)
를 사용하였고, SOCK_CLOSED 와 SOCK_INIT 으로는 진입을 하는데 SOCK_ESTABLISHED 부분으로는 진입을 안하네요.
Data Sheet를 보면 Sn_CR 에서 Connect(0x04)가 되면 Sn_SSR이 SOCK_ESTABLISHED로 변경된다고 되어 있는데,
디버그를 걸어서 보면 계속 SOCK_CLOSED와 SOCK_INIT으로만 상태가 변경됩니다.
SOCK_INIT의 Connect 함수로는 진입이 되고, 해당 함수를
예제문의 ‘connect(s, addr, port)’ 에서
plc_tcp_connect_check=connect(s_ptr->s, PLCIP, 502)
상기 모습으로 변경하였습니다.
Connect가 제대로 되고 있는지 확인할 수 있는 방법과, Established로 진입할 수 있는 방법을 알려주시면 감사드리겠습니다.

하기는 제가 참조한 예시코드입니다.

// void loopback_tcps(SOCKET s, uint16 port, uint8* buf, uint16 mode)
/**

  • “TCP CLIENT” loopback program.
    /
    void loopback_tcpc(SOCKET s, uint8
    addr, uint16 port, uint8* buf, uint16 mode)
    {
    uint32 len;
    static uint16 any_port = 1000;

    switch(getSn_SSR(s)) // check SOCKET status
    { // ------------
    case SOCK_ESTABLISHED: // ESTABLISHED?
    if(getSn_IR(s) & Sn_IR_CON) // check Sn_IR_CON bit
    {
    printf(“%d : Connect OK\r\n”,s);
    setSn_IR(s,Sn_IR_CON); // clear Sn_IR_CON
    }
    if((len=getSn_RX_RSR(s)) > 0) // check the size of received data
    {
    len = recv(s,buf,len); // recv
    if(len !=send(s,buf,len)) // send
    {
    printf(“%d : Send Fail.len=%d\r\n”,s,len);
    }
    }
    break;
    // ---------------
    case SOCK_CLOSE_WAIT: // PASSIVE CLOSED
    disconnect(s); // disconnect
    break;
    // ---------------
    case SOCK_CLOSED: // CLOSED
    close(s); // close the SOCKET
    socket(s,Sn_MR_TCP,any_port++,mode);// open the SOCKET with TCP mode and any source port number
    break;
    // ------------------------------
    case SOCK_INIT: // The SOCKET opened with TCP mode
    connect(s, addr, port); // Try to connect to “TCP SERVER”
    printf(“%d : LOOPBACK_TCPC(%d.%d.%d.%d:%d) Started.\r\n”,s,addr[0],addr[1],addr[2],addr[3],port);
    break;
    default:
    break;
    }
    }

TCP 접속은 아래와 같은 방식으로 접속이 이루어집니다.
TCP client TCP Server
SYN ---------->
<---------- SYN ACK
ACK ------------->

해당 패킷들이 제대로 전달되고 수신되는지 확인 해야 합니다.

Test device와 W5300을 바로 연결하여 테스트 할 경우 패킷을 관찰할 수 없기 때문에,
Test device 대신 PC로 변경 하시고, Wireshark PC program으로 패킷을 관찰하여 보시기 바랍니다.
Wireshark 프로그램은 프리웨어고 인터넷상에 사용법들이 자세히 나와있으니 참고하세요.

plc_tcp_connect_check=connect(s_ptr->s, PLCIP, 502)

s_ptr->s 값이 0~7의 값을 갖는지도 확인바랍니다.

관련하여 PLC와 PC와는 이미 Ethernet 통신(TCP/IP)을 연결하여 ModBus/TCP Protocol 형태로 PC에서 PLC로 명령어를 날리면 PLC에서 PC로 Response를 제대로 날려주는 것을 확인했습니다.
현재는 W5300이 들어가있는 제작보드(Client로 동작) 와 PLC(Server로 동작) 간의 통신을 확인하려는 것입니다.
W5300의 어느 레지스터부분을 확인하면 Connection이 제대로 이루어졌는지 확인 할 수 있을까요?

status register가 Established 가 된다면 연결이 성립된겁니다.

그러나 close 와 init만 반복된다고 하셨는데 어디서 문제가 발생했는지를 확인 하려면 주고 받는 패킷을 확인하는 것이
가장 쉽습니다. 문제점이 어디서 발생하는지는 너무나 많은 변수가 있기 때문입니다.

INIT 상태에서 변경하신 함수를 이용하여 connect를 해야 하는데 패킷은 제대로 나가는지, 패킷의 프로토콜 구성은 제대로 되었는지, 중간에 로스가 발생하지 않는지, PLC에서는 정확한 데이터를 받았는지, 서버에서 응답패킷은 제대로 보냈는지, 응답패킷의 프로토콜 구성은 제대로 되었는지, 응답 패킷을 client가 제대로 받았는지, 제대로 받았는데 파싱을 잘못 했는지 등 확인이 필요합니다.

void loopback_tcpc(SOCKET s, uint8* addr, uint16 port, uint8* buf, uint16 mode)
예에 나와있듯이,
Socket 접속 상태를 확인 하는 방법은 2가지 입니다.
Sn_SSR 의 상태를 모니터링 하거나, Sn_IR(CONNECT) bit가 Set 되었는지 확인 하는 것입니다.

connect() 명령이후 connection이 즉 SOCK_EASTABLISHED 확인이 안된다고 질문을 이해했는데, 아닌가요?

pc와 plc는 잘 되는데, w5300과 plc로 잘 안된다고 이해했습니다.
그래서 w5300과 plc 간의 connection packet들이 잘 오고 가는지 확인차 wireshark로 캡쳐를 부탁드린겁니다.

다시 한번 wireshark로 캡쳐 부탁드립니다.

지속적인 답변 감사드립니다.
추가로 질문이 있습니다.
W5300의 Data Sheet 94쪽에 보면 Socket Initialization 에 대하여 나와 있고, 여기에
‘SOCKET initialization 과정은 “TCP SEVER”와 “TCP CLIENT”의 구분 없이 동일하게 적용된다’
라고 언급되어 있는데, 그럼 TCP SEVER Socket과 TCP CLIENT 소켓을 생성할 때는 동일하게 하면 된다는 의미인가요?
즉 하기와 같은 함수를 이용하여 TCP Sever Socket을 생성하고 있는데(0~6번),
7번도 동일하게 생성한 후 TCP Client Socket으로 사용할 수 있는건가요?
또한 PLC에서는 ModBus TCP를 사용하여 Port Num을 502로 사용하고 있는데 그렇다면 TCP Client Socket의 Port Num도 502로 맞춰줘야 하나요? 아니면 Connect 함수에서만 502번 포트로 연결되도록 하면 되나요?
현재는 Port num을 5007로 만들었고, 하기와 같이 0~7번까지(portnum 5000~5007) 동일하게 Init 처리 하고 있습니다.
또한 Connect 함수에 getSn_IR(s) 함수를 사용하여 Sn_IR값을 읽었는데, 0x00000008 이 나왔습니다.
이는 TimeOut으로 보입니다.
그리고 Connect 진행시 Packet을 주는 부분이 어느 부분인가요?
ModBus TCP는 Protocol이 정해져있어서 정해진 양식으로 전달해야 하지만
PC와 PLC를 Test 할 때 일반 TCP 접속 프로그램을 이용하여 PLC로 접속은 되었습니다.
접속된 이후 ModBus TCP Protocol에 맞게 전달해주면 그에 맞는 response가 넘어왔습니다.
이러한 이유로 Connection시 Packet을 주는 부분은 수정이 없어도 될거라고 생각하는데 아닌가요?

[code]section (“sdram0”) void W5300_sockets_init(void)
{
UINT32 i;

for(i=0; i<8; i++)
{
gSockets[i].s=(SOCKET)i;
gSockets[i].status=SOCK_CLOSED;
gSockets[i].status_prev=SOCK_CLOSED+1; // temp
gSockets[i].port=5000+i; // check CIU_APP.H
gSockets[i].stat_rx_cnt=0;
gSockets[i].stat_tx_cnt=0;
gSockets[i].stat_rx_cnt_total=0;
gSockets[i].stat_tx_cnt_total=0;
gSockets[i].rxb_cnt=0;
gSockets[i].txb_cnt=0;
gSockets[i].rxb_ridx=0;
gSockets[i].rxb_widx=0;
gSockets[i].txb_ridx=0;
gSockets[i].txb_widx=0;
gSockets[i].rxb_max=getIINCHIP_RxMAX(i);
gSockets[i].rxb_ptr=heap_malloc(gHeap_index, gSockets[i].rxb_max);
if(gSockets[i].rxb_ptr==NULL)
DBG_trap(EXCEPTION_USER_GENERIC, “W5300”);
gSockets[i].txb_max=getIINCHIP_TxMAX(i);
gSockets[i].txb_ptr=heap_malloc(gHeap_index, gSockets[i].txb_max);
if(gSockets[i].txb_ptr==NULL)
DBG_trap(EXCEPTION_USER_GENERIC, “W5300”);

// NOTE: if Sn_KPALVTR=0, no KEEP ALIVE is generated
setSn_KPALVTR(i, 0); // val*5sec

}
}[/code]

질문드렸던 loopback_tcpc에 진입하였을 때, Sock_Closed 와 Sock_Init 상태만 반복하는 것은
제가 사용하고 있는 코드에서는 Sock_Closed에 Timeout 설정해 주는 부분이 있었고,
이 부분이 4로 들어가서 0.4mSec만에 Timeout이 되도록 설정되어 있었습니다.
해당 부분을 1초(10000) 으로 세팅하니 Sock_Established 상태로 진입이 되었습니다.
혹시 잘못된 부분이 있다면 말씀해 주시면 감사드리겠습니다.

또한 Sock_Established 상태에서 Sn_IR 이 0x0001 (CON) 이 아닌 0x0010(SendOK) 상태입니다.
SendOK이면 W5300 보드에서 PLC로 TCP Packet은 전달하였는데 Receive가 전달되지 않은건가요?

//IINCHIP_WRITE(RTR, 4+est_num*5); // 100us * X : 변경전 // Timeout 시간 설정 10000(1초)로 하면 Established로 들어가짐 IINCHIP_WRITE(RTR, 10000); // 변경후

안녕하세요

  1. 그럼 TCP SEVER Socket과 TCP CLIENT 소켓을 생성할 때는 동일하게 하면 된다는 의미인가요?
    ->네 그렇습니다. open이후 listen을 하느냐 connect를 하느냐의 차이 입니다.

  2. 7번도 동일하게 생성한 후 TCP Client Socket으로 사용할 수 있는건가요?
    → 네 그렇습니다.

  3. TCP Client Socket의 Port Num도 502로 맞춰줘야 하나요?
    → 포트번호를 맞춰줄 필요가 없습니다. connect함수에서 server의 port number만 맞춰주면 됩니다.
    다만 다른 소켓과 같은 port번호로 생성하지 않으시면 됩니다.
    또한 윈도우 OS 같은경우 한번 connection이 맺어지면 한동안 client와 server의 port번호를 임시로 저장하게 됩니다. 그래서 close이후 똑같은 port번호로 connection을 시도할 경우 안되는 경우가 있습니다. 그래서 일반적인 어플리케이션의 경우 특별한 이유가 없다면, close이후 재 접속할 때 client의 port번호를 증가시키는 방식을 사용합니다. 이 점만 주의 하시면 됩니다.

  4. Connection시 Packet을 주는 부분은 수정이 없어도 될거라고 생각하는데 아닌가요?
    → PC와 PLC간에 일반 TCP 접속 프로그램으로 접속이 되었다면 W5300으로 connect함수를 사용하면 connection이 맺어 집니다. 위 함수를 사용하면 chip 내부적으로 패킷을 주고 받으므로 네트워크에 이상이 없다면 connection이 맺어 질겁니다.

  5. 또한 Sock_Established 상태에서 Sn_IR 이 0x0001 (CON) 이 아닌 0x0010(SendOK) 상태입니다.
    SendOK이면 W5300 보드에서 PLC로 TCP Packet은 전달하였는데 Receive가 전달되지 않은건가요?
    → W5300의 errata 1로 의심이 됩니다. errata를 참조해보시고 그래도 해결이 안된다면 다시 질문 주세요~

감사합니다.

보시기에 이상한 질문도 많을텐데 답변해 주셔서 감사드립니다.

‘5. 또한 Sock_Established 상태에서 Sn_IR 이 0x0001 (CON) 이 아닌 0x0010(SendOK) 상태입니다.
SendOK이면 W5300 보드에서 PLC로 TCP Packet은 전달하였는데 Receive가 전달되지 않은건가요?’
위의 5번에서 언급한 부분은 제가 보고 있는 코드에 Established 부분에서
‘setSn_CR(s_ptr->s, Sn_CR_SEND); ’
부분을 수행하고 나서 0x0010(SendOK) 상태가 되었습니다.
이렇게 수행한 이유를 정확히는 모르겠지만, 데이터시트를 보면
‘Host는 Sn_IR(SENDOK)=1’ 을 확인 후 그 다음 Data에 대한 Send Command를 내릴 수 있다.’
라고 된 것을 보아, Established 상태에서 SendOK 상태로 만들어 준 뒤 필요한 Command를 날리기 위한 것 같습니다.
해당 구문을 하기 예시문 상황에서 case Sock_Established 밑에 if(getSn_IR(s) & Sn_IR_CON) 내부의
setSn_IR(s,Sn_IR_CON); 구문을 수행한 뒤

setSn_TX_WRSR(s_ptr->s, 1); IINCHIP_WRITE(Sn_TX_FIFOR(s_ptr->s), 0x0000); setSn_CR(s_ptr->s, Sn_CR_SEND);

이러한 명령어를 수행하고 나면 Sn_IR(s)가 0x0010 이 되었습니다.
궁금한건 Estatblished 들어갈 때는 Sn_IR(s)가 0x0001 이었다가
setSn_IR(s,Sn_IR_CON); 구문을 수행한 뒤 0x0000 이 되고
이후에 다시 setSn_CR(s_ptr->s, Sn_CR_SEND); 을 하면 Sn_IR(s)가 0x0010 이 되는 것입니다.
W5300에서 제공한 예시코드를 봐도 Established 내부에
’ setSn_IR(s,Sn_IR_CON); // clear Sn_IR_CON’ 라고 되어 있는데 왜 이부분을 사용해서
Sn_IR(s)를 0x0001 (Connect) 상태에서 0x0000 상태로 변경시켜 주나요??
또한 Sn_IR(s)가 0x0010 인 상태에서 원하는 Data Send가 가능한게 맞나요?

그리고 만약 제가 ModBus TCP protocol에 맞춰 W5300사용 보드에서 PLC로 [0000 0000 0006 01 03 0001 0001] 와 같은 Data를 보내고(Send),
(즉 W5300보드(Client) to PLC Send Data(Server))
[ 0000 0000 0006 01 //MBAP Header
03 //Function Code (Read : 03 / Write : 06)
0001 0001 // DATA ]

이에 따라 PLC에서 W5300사용 보드로 [0000 0000 0005 01 03 02 00C8] 라는 Data를 준다면(Recieve)
어떻게 보내고 받을 수 있을까요?
Data를 Send하는 부분은 Established 구문 안에 넣고 loopback_tcpc 문이 원하는 시간마다 반복하게끔 해주면 될까요?
또한 Recieve 받는 부분은 Send 하는 부분 바로 다음 문장에 구현하면 될까요?
Send, Recieve 처리에 대한 부분을 상세히 알려주시면 감사드리겠습니다.

(즉 W5300사용 보드 to PLC Send Data)
[ 0000 0000 0005 01 //MBAP Header
03 //Function Code (Write 06)
02 00C8 // DATA ]

[code]
// void loopback_tcps(SOCKET s, uint16 port, uint8* buf, uint16 mode)
/**

  • “TCP CLIENT” loopback program.
    /
    void loopback_tcpc(SOCKET s, uint8
    addr, uint16 port, uint8* buf, uint16 mode)
    {
    uint32 len;
    static uint16 any_port = 1000;

    switch(getSn_SSR(s)) // check SOCKET status
    { // ------------
    case SOCK_ESTABLISHED: // ESTABLISHED?
    if(getSn_IR(s) & Sn_IR_CON) // check Sn_IR_CON bit
    {
    printf(“%d : Connect OK\r\n”,s);
    setSn_IR(s,Sn_IR_CON); // clear Sn_IR_CON
    }
    if((len=getSn_RX_RSR(s)) > 0) // check the size of received data
    {
    len = recv(s,buf,len); // recv
    if(len !=send(s,buf,len)) // send
    {
    printf(“%d : Send Fail.len=%d\r\n”,s,len);
    }
    }
    break; [/code]

[quote] setSn_IR(s,Sn_IR_CON); // clear Sn_IR_CON’ 라고 되어 있는데 왜 이부분을 사용해서
Sn_IR(s)를 0x0001 (Connect) 상태에서 0x0000 상태로 변경시켜 주나요??[/quote]
Sn_IR(Connect) bit는 접속이 성공했을때, 한번 Set되어지는 Interrupt status bit입니다. Sn_IR 값이 0x00이 아닌 경우 W5300의 Interrupt PIN이 Assert됩니다. MCU에서 Interrupt 방식으로 구현할 경우, Interrupt 발생을 감지하고, 해당 Interrupt가 무엇인지 확인이 필요합니다. 이때 사용하는 상태 Bit가 Sn_IR의 각 bit입니다.인터럽트관련 자세한 내용은 데이타시트를 참고하세요.

Sn_IR(SENDOK) 비트는 데이타 전송 COMMAND를 수행했을 경우 1로 설정됩니다. 한가지 주의사항은 SENDOK가 1이라고 하더라도 실제 Data가 Ethernet으로 전송이 되었다는 것은 아닙니다. 즉 W5300 내부적으로 Data를 인식하였고, 이를 Ethernet상으로 보낼 준비가 완료되었음을 의미합니다. 사용자는 Data Send command를 수행하고 반드시 Command Clear와 SendOK를 확인하셔야 합니다.

[quote]이에 따라 PLC에서 W5300사용 보드로 [0000 0000 0005 01 03 02 00C8] 라는 Data를 준다면(Recieve)
어떻게 보내고 받을 수 있을까요?
Data를 Send하는 부분은 Established 구문 안에 넣고 loopback_tcpc 문이 원하는 시간마다 반복하게끔 해주면 될까요?
또한 Recieve 받는 부분은 Send 하는 부분 바로 다음 문장에 구현하면 될까요?
Send, Recieve 처리에 대한 부분을 상세히 알려주시면 감사드리겠습니다.
[/quote]
Send는 앞서 말씀드린대로, Data를 Sn_TX_FIFOR로 Copy후 Send Size를 Sn_TX_WRSR로설정하고, Send command를 Sn_CR를 통해 수행 후, Sn_CR(s) 가 0일때까지 Wait, Sn_IR(SENDOK) = '1’일때까지 Wait 하시면 됩니다.

Recevie의 경우는 Sn_IR(RECV)=‘1’(인터럽트방식)를 확인하시거나, Sn_RX_RSR 값이 0이 아닌 경우를 확인하시면 됩니다.
데이타 수신을 감지한 경우, Sn_RX_RSR 값 만큼, Sn_RX_FIFOR로부터 Data를 수신하고, Recv_command를 Sn_CR를 통해 수행하고, Sn_CR(s)가 0일때까지 Wait합니다.

또한 질문하신 주기적 전송은 해당 주기마다 전송을 수행하시면 됩니다. 수신은 Send 이후 확인해도 무방합니다.