---
title: "stm32f103 w5500 tcp server"
url: "https://maker.wiznet.io/WIZnet/projects/stm32f103-w5500-tcp-server/"
markdown_url: "https://maker.wiznet.io/WIZnet/projects/stm32f103-w5500-tcp-server/md"
type: "UCC: User Created Content"
author: "donglicaiju76152"
author_url: "https://www.twblogs.net/a/5d3fa2c4bd9eee51fbf93ce2"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "donglicaiju76152"
original_url: "https://www.twblogs.net/a/5d3fa2c4bd9eee51fbf93ce2"
published: "2023-02-17"
language: "en"
likes: 4
views: 514
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# stm32f103 w5500 tcp server

> mult TCP clients supported TCP server

Original author: donglicaiju76152 (source: https://www.twblogs.net/a/5d3fa2c4bd9eee51fbf93ce2)

## Article

**DETAILS**

## **背景**

項目中需要使用網絡，開始使用的stm32f4+lwip的方案，但是硬件成本有些高，更主要的是lwip不好用，老是斷，可能是自己沒有研究透它吧。經過長時間的調研論證，最終選擇了w5500這款芯片。它把TCP/IP網絡協議棧固化在了硬件芯片中，爲用戶留出應用層接口，簡單穩定。

## **移植過程**

首先，從https://w5500.com/上下載芯片手冊和參考代碼瞭解芯片的原理及基本用法，內容不是很複雜。項目中需要W5500作爲TCP Server，會有三個客戶端連接。參考相關代碼，移植到項目中，具體代碼如下。

#### **配置**

```c
static void net_set_config(void)
{
    unsigned char mac[6] = {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11};
    unsigned char ip[4]  = {192, 168, 1, 100};
    unsigned char sub[4] = {255, 255, 255, 0};
    unsigned char gw[4]  = {192, 168, 1, 1};
    unsigned char tx_size[8] = {2,2,2,2,2,2,2,2};
    unsigned char rx_size[8] = {2,2,2,2,2,2,2,2};

    /* 設置Mac地址 */
    set_mac_addr(mac);
    /* 設置IP */
    set_source_ip_addr(ip);
    /* 設置子網掩碼 */
    set_subnet_mask(sub);
    /* 設置網關 */
    set_gateway(gw);

    /* 初始化8個socket */
    sys_init(tx_size, rx_size);
    /* 設置超時時間 */
    set_retrans_time(2000);
    /* 設置最大重新發送次數 */
    set_retrans_num(3);
	
    /* 打開心跳功能 */
    set_keepalive(SOCKET_ID_0);
	set_keepalive(SOCKET_ID_1);
	set_keepalive(SOCKET_ID_2);	
}
```

#### **狀態機**

```c
int net_process_socket(SOCKET sock, unsigned short port)
{
    int ret = -1;
    unsigned char state = SOCK_CLOSED;
    unsigned short len = 0;
    unsigned char data[MAX_MSG_LEN];

    state = get_sock_status(sock);
    switch (state) {
        case SOCK_INIT:
            listen(sock);
            break;
        case SOCK_ESTABLISHED:
            if (get_sock_interrupt_status(sock) & Sn_IR_CON) {
                set_sock_interrupt_status(sock, Sn_IR_CON);
            }
            len = get_sock_rx_free_buff_size(sock);
            if (len > 0) {
                recv(sock, data, MAX_MSG_LEN);
				/* process the recv data */
            }
            break;
        case SOCK_CLOSE_WAIT:
            disconnect(sock);
            break;
        case SOCK_CLOSED:
            ret = socket(sock, Sn_MR_TCP, port, Sn_MR_ND);
            break;
        default:
            break;
    }

    return 0;
}
```

#### **運行**

```c
void net_task(void *arg)
{    
    /* 配置網絡信息 */
    net_set_config();

    while (1) { 
        net_process_socket(SOCKET_ID_0, 12345);
        net_process_socket(SOCKET_ID_1, 12345);
        net_process_socket(SOCKET_ID_2, 12345);
		
		/* 延時20ms */
        vTaskDelay(100);
    }
}
```

w5500最多支持8個socket連接，由於項目中需要三個客戶端，所以，配置了三個socket。然後，在單獨的線程中依次運行每個socket對應的狀態機。具體的基礎知識不去細說，官網和博客中有非常多的描述。

## **問題**

按說，按照官網的設置後，應該可以正常運行。確實可以運行了，三個客戶端也都可以連接上，但是，設備長時間測試中發現，客戶端和服務器之間的連接經常斷開。一旦斷了，有的客戶端可以連上，有的客戶端就再也連接不上了。解決的方式只能是重啓設備，這樣肯定是不行的啊。

客戶端是有重連機制的，會週期地發送心跳包給服務器，超時後就重新連接服務器，整個機制都沒有問題，問題就是始終連接不上服務器了。採用的有線連接，按說本身就不應該斷開的。這個問題折騰了一個月多都沒有頭緒，設備出廠的日期也只能往後推了。

## **解決方案**

首先，通過網絡調試助手模擬客戶端，長時間運行看看會不會斷，沒有斷過。通過wireshark抓包，發現沒有keepalive心跳包，不知道爲什麼服務器沒有發出來。參考了同事和浩然電子杜工的建議，進行了如下修改，問題得到解決，目前運行十幾天，設備沒有斷過一次。

- 增加備用socket
  有三個客戶端，那麼就開六個客戶端，每個客戶端有一個正常使用的，一個備用的，一旦連接斷掉，另一個立馬啓用，無縫切換。

- 心跳處理
  自動發送心跳改成手動發送。

- 增加延時
  官網上的資料包均是基於裸機的演示程序，而我們的項目是多線程的，所以，需要在死等的地方修改成延時。

#### **配置**

```c
static void net_set_config(void)
{
    unsigned char mac[6] = {0x00, 0x08, 0xdc, 0x11, 0x11, 0x11};
    unsigned char ip[4]  = {192, 168, 1, 100};
    unsigned char sub[4] = {255, 255, 255, 0};
    unsigned char gw[4]  = {192, 168, 1, 1};
    unsigned char tx_size[8] = {2,2,2,2,2,2,2,2};
    unsigned char rx_size[8] = {2,2,2,2,2,2,2,2};

    /* 設置Mac地址 */
    set_mac_addr(mac);
    /* 設置IP */
    set_source_ip_addr(ip);
    /* 設置子網掩碼 */
    set_subnet_mask(sub);
    /* 設置網關 */
    set_gateway(gw);

    /* 初始化8個socket */
    sys_init(tx_size, rx_size);
    /* 設置超時時間 */
    set_retrans_time(2000);
    /* 設置最大重新發送次數 */
    set_retrans_num(3);	
}
```

不去開啓心跳自動發送機制。

#### **狀態機**

```c
int net_process_socket(SOCKET sock, unsigned short port)
{
    int ret = -1;
    unsigned char state = SOCK_CLOSED;
    unsigned short len = 0;
    unsigned char data[MAX_MSG_LEN];
    static unsigned char sock_est_flag[8] = {1, 1, 1, 1, 1, 1, 1, 1};
    SOCKET another_sock;

    state = get_sock_status(sock);
    switch (state) {
        case SOCK_INIT:
            listen(sock);
            sock_est_flag[sock] = 1;
            break;
        case SOCK_ESTABLISHED:
            if (sock_est_flag[sock] == 1) {
                sock_est_flag[sock] = 0;
                if ((sock % 2) == 0) {
                    another_sock = sock + 1;
                } else {
                    another_sock = sock - 1;
                }
                if (get_sock_status(another_sock) == SOCK_ESTABLISHED) {
                    close(another_sock);
                }
            }
            
            if (get_sock_interrupt_status(sock) & Sn_IR_CON) {
                set_sock_interrupt_status(sock, Sn_IR_CON);
            }
            len = get_sock_rx_free_buff_size(sock);
            if (len > 0) {
                recv(sock, data, MAX_MSG_LEN);
                /* process the recv data */
            }
            break;
        case SOCK_CLOSE_WAIT:
            disconnect(sock);
            break;
        case SOCK_CLOSED:
            ret = socket(sock, Sn_MR_TCP, port, Sn_MR_ND);
            break;
        default:
            break;
    }

    return 0;
}
```

增加備用socket。備用socket時刻處於listen狀態。

#### **運行**

```c
void net_task(void *arg)
{    
	int i = 0;
	int counter = 0;

    /* 配置網絡信息 */
    net_set_config();

    while (1) { 
        net_process_socket(SOCKET_ID_0, 12345);
        net_process_socket(SOCKET_ID_1, 12345);
        net_process_socket(SOCKET_ID_2, 12346);
        net_process_socket(SOCKET_ID_3, 12346);
        net_process_socket(SOCKET_ID_4, 12347);
        net_process_socket(SOCKET_ID_5, 12347);

		if ((counter++ % 100) == 0) {
			for (i = 0 ; i < 6; i++) {
				iinchip_write_data(Sn_CR(i),Sn_CR_SEND_KEEP);
			}
		}
		
		/* 延時20ms */
        vTaskDelay(100);
    }
}
```

手動發送keepalive心跳包，每10s發送一次。

#### **延時**

另外， 非常重要的一點是，在socket的發送和接收函數中死等的地方增加操作系統的延時。

## **總結**

遇到問題不要着急，總會有解決的方案，現在沒有隻是時候未到。說明目前掌握的線索還不足以破案，需要繼續分析研究。黔驢技窮後多和同事請教討論，很多時候會思維定式，別人的某句話可能就能給你提供有用的線索。

再次感謝浩然電子的杜工耐心的幫助。
再此記錄下來艱辛歷程，希望可以幫到其他人。

---

Source: https://maker.wiznet.io/WIZnet/projects/stm32f103-w5500-tcp-server/
