WIZnet Developer Forum

W5500 여러대 동시 접속시 충돌 문제

현재 W5500 장치 3대를 공유기에 물려서 서버로 데이터를 전송하고 있습니다.
DHCP 방식이 아닌 static 방식으로,
192.168.1.220
192.168.1.221
192.168.1.222
이렇게 설정해서 사용하고 있습니다.
3대를 동시에 켜지 않고, 따로따로 켰을 경우에는 일단 문제 없이 잘 작동합니다. 그러나, 3대를 동시에 켰을 경우 충돌이 발생해서 오작동합니다. 혹시 몰라 1대를 빼고 2대만 동시에 켜도 같은 문제가 발생합니다.
도움 부탁드립니다.

안녕하세요

문의 주신 내용에 답변 드립니다

  1. 서버 쪽 포트 번호 확인
  2. W5500의 MAC 주소가 중복되어 충돌하는 건 아닌 지

확인 부탁드리겠습니다

감사합니다

서버쪽 포트는 9001 사용하고 있습니다.
MAC 주소도 다 다르게 확인이 됩니다.

제가 (W5500)장치 3대를
Server (port: 9001) 192.168.1.10

Client_1 : 192.168.1.220
Client_2 : 192.168.1.221
Client_3 : 192.168.1.222

맞물려 서버에 전송했을 때 서버에선 문제 없이 데이터를 받고 있습니다.
어떤 환경에서 작업하고 계시는 지 알 수 있을까요?

MCU의 main함수에서 while 루프 진입 전에 IO_LIBRARY_INIT 함수로 초기화한 이후
while 루프 안에서 5분마다 한번씩 데이터를 서버로 TCP 전송하고 있습니다.

connect함수와 recv함수에서 연결이 없거나 응답이 없을 시 무한 루프를 막기위해
다음과 같이 카운터를 삽입하여 정해진 횟수를 초과하면 루프를 빠져나가도록 했습니다.
이것 때문에 동시 접속이나 동시 전송이 이루어졌을 때 문제가 발생하는 것일까요?

int8_t connect(uint8_t sn, uint8_t * addr, uint16_t port)
{
CHECK_SOCKNUM();
CHECK_SOCKMODE(Sn_MR_TCP);
CHECK_SOCKINIT();
uint32_t taddr;
taddr = ((uint32_t)addr[0] & 0x000000FF);
taddr = (taddr << 8) + ((uint32_t)addr[1] & 0x000000FF);
taddr = (taddr << 8) + ((uint32_t)addr[2] & 0x000000FF);
taddr = (taddr << 8) + ((uint32_t)addr[3] & 0x000000FF);
if( taddr == 0xFFFFFFFF || taddr == 0) return SOCKERR_IPINVALID;
if(port == 0) return SOCKERR_PORTZERO;
setSn_DIPR(sn,addr);
setSn_DPORT(sn,port);
setSn_CR(sn,Sn_CR_CONNECT);
while(getSn_CR(sn));
if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
uint16_t timeout_ = 0;

while(getSn_SR(sn) != SOCK_ESTABLISHED)
{
	if ((getSn_IR(sn) & Sn_IR_TIMEOUT) || (timeout_ > CONNECT_CNT))
	{
		setSn_IR(sn, Sn_IR_TIMEOUT);
		return SOCKERR_TIMEOUT;
	}
	if (getSn_SR(sn) == SOCK_CLOSED)
	{
		return SOCKERR_SOCKCLOSED;
	}
	if (getSn_SR(sn) == SOCK_SYNSENT)	timeout_++;
}
return SOCK_OK;

}

int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len)
{
uint8_t tmp = 0;
uint16_t cnt = 0;
uint16_t recvsize = 0;

CHECK_SOCKNUM();
CHECK_SOCKMODE(Sn_MR_TCP);
CHECK_SOCKDATA();
recvsize = getSn_RxMAX(sn);
if(recvsize < len) len = recvsize;

while(cnt < RECV_CNT)
{
	recvsize = getSn_RX_RSR(sn);
	tmp = getSn_SR(sn);
	if (tmp != SOCK_ESTABLISHED)
	{
		if(tmp == SOCK_CLOSE_WAIT)
		{
			if(recvsize != 0) break;
			else if(getSn_TX_FSR(sn) == getSn_TxMAX(sn))
			{
				close(sn);
				return SOCKERR_SOCKSTATUS;
			}
		}
		else
		{
			close(sn);
			return SOCKERR_SOCKSTATUS;
		}
	}
	if((sock_io_mode & (1<<sn)) && (recvsize == 0)) return SOCK_BUSY;
	if(recvsize != 0) break;
	cnt++;
}
if(cnt >= RECV_CNT)	return SOCKERR_TIMEOUT;
if(recvsize < len) len = recvsize;
wiz_recv_data(sn, buf, len);
setSn_CR(sn,Sn_CR_RECV);
while(getSn_CR(sn));
return (int32_t)len;

}

서버쪽 문제가 아닐까 생각되는데,
서버 쪽을 (Hercules)와 같은 상용 프로그램으로 먼저 제품 3대가 connect 되는 지 테스트 진행해볼 수 있을까요?
하기 링크에서 다운로드하여 먼저 테스트 진행 부탁 드리겠습니다.
감사합니다
https://www.hw-group.com/software/hercules-setup-utility

감사합니다. Hercules로 테스트 해봐야겠네요.

서버(Hercules)에서 데이터 전송했을 때 각 클라이언트에서 데이터를 받을 수 있는 것도 확인할 수 있습니다.

감사합니다

답변 감사합니다.
아무래도 알고리즘의 다른 부분에서 충돌요인이 발생하는 듯 하네요.

확인 결과 접속에서 문제가 발생하는 것이 아니라, 여러개의 W5500 모듈에서 동시에 서버로 TCP 전송이 이루어질 때 발생하더군요.
TCP 전송 코드는 아래와 같습니다.

void TCP_send(uint8_t num)
{
if(wizphy_getphylink() == PHY_LINK_ON)
{
if(socket(SOCK_TCPS, Sn_MR_TCP, SERVER_port, 0) == SOCK_TCPS)
{
if(connect(SOCK_TCPS, TEST_IP, SERVER_port) == SOCK_OK)
{
for(uint8_t i=0; i<num; i++)
{
SRAM23_readBlock(i * (PAYLOAD_LENGTH + 14), PAYLOAD_LENGTH + 14, SENSOR_DATA);
CRC_dataSet();

				CRC_TEMP = CalcCRC(CRC_METER_DATA, sizeof(CRC_METER_DATA));
				CRC_METER_FIX[72] = (uint8_t)(CRC_TEMP >> 8);
				CRC_METER_FIX[71] = (uint8_t)CRC_TEMP;
		
				send(SOCK_TCPS, CRC_METER_FIX, sizeof(CRC_METER_FIX));
				recv(SOCK_TCPS, CRC_RECV_DATA, sizeof(CRC_RECV_DATA));

				if(CheckCRC(CRC_RECV_DATA, sizeof(CRC_RECV_DATA)))
				{

				}
				close(SOCK_TCPS);
				for(uint8_t i=0; i<sizeof(CRC_RECV_DATA); i++)	CRC_RECV_DATA[i] = 0;
			}
			disconnect(SOCK_TCPS);
		}
	}
}

}

SRAM에서 데이터를 읽어와 num만큼 TCP로 전송하게 됩니다.
이것이 각 W5500 모듈에서 동시에 발생하면 패킷이 빠지거나 에러가 발생합니다.
허큘리스에서 TCP 다중 접속이 불가능해서 그런건지, 아니면 코드에 문제가 있는건지 모르겠습니다.
한 개의 W5500모듈에서만 전송이 이루어질 때는 전혀 문제가 없습니다.
도움 부탁드려요.

혹시, send, recv 함수 앞에
getSn_SR(SOCK_TCPS) == SOCK_ESTABLISHED 가 될 때까지 기다리는 처리를 해야 할까요?

1.send/recv의 경우 Sn_SR의 상태가 SOCK_Established가 되어야하며,
Sn_CR를 내릴때마다 Sn_IR를 체크하시기를 추천드립니다.
2. Sn_CR= connect 내린 후 Sn_SR값이 Sock_Established 된 후 Sn_IR의 Sn_IR_CON bit를 클리어 해 줘야합니다.

TCP Client의 동작은 TCP Client 루프백동작인 loopback_tcpc()함수 참고하여 작성하시길바랍니다.

저희회사 정책상 코드리뷰를 하고 있지 않다는 점 양해 부탁드립니다.

  1. 공유기 없이 PC과 보드를 랜케이블을 직접연결한 후 Wireshake tool(freeware) 을 이용하여 문제가 발생한 네트워크 패킷을 업로드 해주시면 코드 리뷰는 아니지만 문제해결에 대한 빠른 대응이 될것같습니다.

Copyright © 2017 WIZnet Co., Ltd. All Rights Reserved.