Building a TCP Multi-Socket Server with W55RP20 & W5500
Building a TCP Multi-Socket Server with W55RP20 & W5500

Use Cases for Multiple TCP Sockets
1. Handling Multiple Clients Simultaneously
- Each client needs an independent communication channel with the server.
- ex) Chat applications, multiplayer games, web servers.
2. Separation of Data Types
- Different sockets can handle control signals (e.g., commands) and data streams (e.g., file transfer) separately for better organization.
- ex) Video conferencing apps (control vs. media streams).
3. Error Isolation
- Problems in one socket (e.g., a failed client connection) won't affect other connections.
- ex) IoT systems with multiple devices sending data to a server.
4. Scalability for Large User Bases
- Servers need multiple sockets to support thousands of concurrent users or devices.
- ex) Large-scale web platforms, cloud services, or game servers.
https://github.com/WIZnet-ioNIC/WIZnet-PICO-C/tree/main/examples/tcp_server_multi_socket
I will use the example from the above link to establish communication between the W55RP20-EVB-Pico and W5500-EVB-Pico.
The example in the above link demonstrates a TCP loopback server capable of opening multiple sockets. However, in this WCC, I will slightly modify the approach:
the W55RP20 will function as a TCP server, capable of opening up to eight multi-sockets, while the W5500 will act as a TCP client, connecting to the W55RP20 for communication.
Among the multiple sockets, socket 0 will be responsible for transmitting messages regarding the status of the multi-sockets, while the remaining sockets will operate as loopback servers.
Let's start by setting up the server.
When you first run the tcp_server_multi_socket
example, you will see that sockets 0 to 7 are in the listen state, as shown in the following output.
In this basic example, immediately after a specific socket connection is established, we check how many sockets are actively communicating.
The number of sockets that are in the established state is sent to socket 0. Note that since socket numbers are assigned sequentially in this example, the first client that connects to the server will be assigned to socket 0.
Additionally, a message is also sent when a socket is closed, as shown in the following modification.
https://github.com/WIZnet-ioNIC/WIZnet-PICO-C/tree/main/examples/loopback
The following is the execution screen. In addition to the W5500, the TCP client was tested by creating two additional clients using Hercules.
When the client is first executed, it connects to the server, and the following screen is displayed.
In the client screen, the message "0sn is connected. total:1socket" is shown, indicating that socket 0 has been connected to the server and a total of one socket is in the established state.
The following is the screen showing another client connecting and then closing. Each client sent one message before disconnecting.
The client on socket 0 receives the status messages as follows.
- The following is the code for the TCP server.
#include <stdio.h>
#include <string.h>
#include "port_common.h"
#include "wizchip_conf.h"
#include "w5x00_spi.h"
#include "socket.h"
/* Clock */
#define PLL_SYS_KHZ (133 * 1000)
/* Buffer */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) // Send and receive cache size
#define _LOOPBACK_DEBUG_
#define _SOCKETS_5100S 4
#define _SOCKETS_5500 8
wiz_NetInfo net_info = {
.mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x11}, // Define MAC variables
.ip = {192, 168, 11, 33}, // Define IP variables
.sn = {255, 255, 255, 0}, // Define subnet variables
.gw = {192, 168, 11, 1}, // Define gateway variables
.dns = {168, 126, 63, 1}, // Define DNS variables
.dhcp = NETINFO_STATIC}; // Define the DNCP mode
static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
0,
};
static uint16_t local_port = 8000; // Local port
static void set_clock_khz(void);
int32_t loopback_tcps_multi_socket(uint8_t *buf, uint16_t port);
int main()
{
set_clock_khz();
/*mcu init*/
stdio_init_all(); // Initialize the main control peripheral.
wizchip_spi_initialize();
wizchip_cris_initialize();
wizchip_reset();
wizchip_initialize(); // spi initialization
wizchip_check();
network_initialize(net_info);
print_network_information(net_info); // Read back the configuration information and print it
while (true)
{
loopback_tcps_multi_socket(ethernet_buf, local_port);
}
}
static void set_clock_khz(void)
{
// set a system clock frequency in khz
set_sys_clock_khz(PLL_SYS_KHZ, true);
// configure the specified clock
clock_configure(
clk_peri,
0, // No glitchless mux
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
PLL_SYS_KHZ * 1000, // Input frequency
PLL_SYS_KHZ * 1000 // Output (must be same as no divider)
);
}
int32_t loopback_tcps_multi_socket(uint8_t *buf, uint16_t port)
{
static uint8_t sn = 0;
int32_t ret;
uint16_t size = 0, sentsize = 0;
uint8_t destip[4];
uint16_t destport;
uint8_t state_buf[1024];
int cnt = 0;
switch (getSn_SR(sn))
{
case SOCK_ESTABLISHED:
if (getSn_IR(sn) & Sn_IR_CON)
{
getSn_DIPR(sn, destip);
destport = getSn_DPORT(sn);
printf("%d:Connected - %d.%d.%d.%d : %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
setSn_IR(sn, Sn_IR_CON);
if (_WIZCHIP_ == W5500)
{
for (int i = 0; i < _SOCKETS_5500; i++)
{
if (SOCK_ESTABLISHED == getSn_SR(i))
{
cnt++;
}
}
memset(state_buf, 0, sizeof(state_buf));
snprintf((char *)state_buf, sizeof(state_buf), "**%dsn is Connected. **total:%dsockets\r\n", sn, cnt);
send(0, state_buf, sizeof(state_buf));
}
else if (_WIZCHIP_ == W5100S)
{
for (int i = 0; i < _SOCKETS_5100S; i++)
{
if (SOCK_ESTABLISHED == getSn_SR(i))
{
cnt++;
}
}
memset(state_buf, 0, sizeof(state_buf));
snprintf((char *)state_buf, sizeof(state_buf), "**%dsn is Connected. **total:%dsockets\r\n", sn, cnt);
send(0, state_buf, sizeof(state_buf));
}
}
if (sn != 0 && (size = getSn_RX_RSR(sn)) > 0) // Don't need to check SOCKERR_BUSY because it doesn't not occur.
{
if (size > ETHERNET_BUF_MAX_SIZE)
size = ETHERNET_BUF_MAX_SIZE;
ret = recv(sn, buf, size);
if (ret <= 0)
return ret; // check SOCKERR_BUSY & SOCKERR_XXX. For showing the occurrence of SOCKERR_BUSY.
size = (uint16_t)ret;
buf[size] = 0x00;
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.
}
getSn_DIPR(sn, destip);
destport = getSn_DPORT(sn);
printf("socket%d from:%d.%d.%d.%d port: %d message:%s\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport, buf);
}
break;
case SOCK_CLOSE_WAIT:
#ifdef _LOOPBACK_DEBUG_
// printf("%d:CloseWait\r\n",sn);
#endif
if ((ret = disconnect(sn)) != SOCK_OK)
return ret;
#ifdef _LOOPBACK_DEBUG_
memset(state_buf, 0, sizeof(state_buf));
snprintf((char *)state_buf, sizeof(state_buf), "**%d:Socket Closed\r\n", sn);
send(0, state_buf, sizeof(state_buf));
printf("%d:Socket Closed\r\n", sn);
#endif
break;
case SOCK_INIT:
#ifdef _LOOPBACK_DEBUG_
printf("%d:Listen, TCP server loopback, port [%d]\r\n", sn, port);
#endif
if ((ret = listen(sn)) != SOCK_OK)
return ret;
break;
case SOCK_CLOSED:
#ifdef _LOOPBACK_DEBUG_
// printf("%d:TCP server loopback start\r\n",sn);
#endif
if ((ret = socket(sn, Sn_MR_TCP, port, Sn_MR_ND)) != sn)
return ret;
#ifdef _LOOPBACK_DEBUG_
// printf("%d:Socket opened\r\n",sn);
#endif
break;
default:
break;
}
#if (_WIZCHIP_ == W5100S)
if (sn < _SOCKETS_5100S)
{
sn++;
}
else
{
sn = 0;
}
#elif (_WIZCHIP_ == W5500)
if (sn < _SOCKETS_5500)
{
sn++;
}
else
{
sn = 0;
}
#endif
return 1;
}