W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端

系列文章

一、实现思路

W5500内部是硬件TCP/IP协议栈,对外(MCU)只是提供了操作socket的能力,内部支持8个独立的socket,每一个socket通过Socket n寄存器区控制(0≤n≤7)

所以在编写基于Socket的网络应用程序时,可以按照查询Socket状态寄存器实现一个状态机的思路来实现

W5500驱动库中提供了Socket状态寄存器的读取宏:

/**
 * @ingroup Socket_register_access_function
 * @brief Get @ref Sn_SR register
 * @param (uint8_t)sn Socket number. It should be <b>0 ~ 7</b>.
 * @return uint8_t. Value of @ref Sn_SR.
 */
#define getSn_SR(sn) \
		WIZCHIP_READ(Sn_SR(sn))

W5500驱动库对读取出的状态值也提供了宏定义:

/* Sn_SR values */
#define SOCK_CLOSED                  0x00
#define SOCK_INIT                    0x13
#define SOCK_LISTEN                  0x14
#define SOCK_SYNSENT                 0x15	//Socket n状态改变时的临时状态,已经发送连接请求到对方
#define SOCK_SYNRECV                 0x16	//Socket n状态改变时的临时状态,成功接收到了连接请求包
#define SOCK_ESTABLISHED             0x17	
#define SOCK_FIN_WAIT                0x18	//Socket n状态改变时的临时状态,socket正在关闭
#define SOCK_CLOSING                 0x1A	//Socket n状态改变时的临时状态,socket正在关闭
#define SOCK_TIME_WAIT               0x1B	//Socket n状态改变时的临时状态,socket正在关闭
#define SOCK_CLOSE_WAIT              0x1C
#define SOCK_LAST_ACK                0x1D	//Socket n状态改变时的临时状态,socket在被动关闭状态下,正在等待对断开连接请求做出回应

二、TCP 服务端的实现

1. 实现功能

实现一个TCP服务端,可以接收TCP客户端的请求,在收到客户端数据之后打印,并回发收到的数据。

2. TCP服务端状态机

3. 实现代码

编写demo文件tcp_server.c

#include "socket.h"
#include <stdio.h>

#define DATA_BUF_SIZE			2048
static uint8_t recv_buf[DATA_BUF_SIZE];

int tcp_server(uint8_t sn, uint16_t port)
{
    int8_t ret;
    uint8_t dest_ip[4];
    uint16_t dest_port;
    uint16_t size = 0;
    
    switch (getSn_SR(sn)) {
    
        case SOCK_CLOSED:
            /* open socket */
            printf("TCP server start\r\n");
            if ((ret = socket(sn, Sn_MR_TCP, port, 0x00)) != sn) {
                printf("socket %d open fail\r\n", sn);
                return ret;
            }
            printf("socket %d open success\r\n", sn);
            break;
        
        case SOCK_INIT:
            /* waiting for a client to connect */
            printf("listen %d port...\r\n", port);
            if ((ret = listen(sn)) != SOCK_OK) {
                printf("%d:listen fail\r\n", sn);
                return ret;
            }
            printf("%d:listen success\r\n", sn);
            break;
        case SOCK_ESTABLISHED:
            /* socket has been established */
            if(getSn_IR(sn) & Sn_IR_CON) {
                getSn_DIPR(sn, dest_ip);
                dest_port = getSn_DPORT(sn);
                printf("%d:a client connect success, %d.%d.%d.%d:%d\r\n", sn, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], dest_port);
                
                setSn_IR(sn,Sn_IR_CON);
            }
            
            // get the size of recv data in recv buffer
            if ((size = getSn_RX_RSR(sn)) > 0) {
                if (size > DATA_BUF_SIZE) {
                    size = DATA_BUF_SIZE;
                }
                
                //recv data
                ret = recv(sn, recv_buf, DATA_BUF_SIZE);     
                if (ret <= 0) {
                    printf("%d:recv fail\r\n", sn);
                    return ret;
                } else {
                    // The actual received size
                    size = (uint16_t)ret;
                    printf("%d:recv size:%d\r\n", sn, size);
                    recv_buf[size] = '\0';
                    printf("%d:recv data:[%s]\r\n", sn, recv_buf);
                }
                
                //send resp data
                ret = send(sn, recv_buf, size);
                if (ret <= 0) {
                    printf("%d:send fail\r\n", sn);
                    
                    //close the socket
                    close(sn);
       
                    return ret;
                } else {
                    // The actual sent size
                    size = (uint16_t)ret;
                    printf("%d:send size:%d\r\n", sn, size);
                    recv_buf[size] = '\0';
                    printf("%d:send data:[%s]\r\n", sn, recv_buf);
                }   
            }
            break;
            
        case SOCK_CLOSE_WAIT:
            /* closing the socket */
            if ((ret = disconnect(sn)) != SOCK_OK) {
                printf("%d:disconnect fail\r\n", sn);
                return ret;
            }
            printf("%d: socket is closed\r\n", sn);
            break;
        
        default:
            break;
    }
    
    return 0;
}

4. 实现效果

将编写的demo文件加入到工程中,在main.c中声明一下该测试函数:

extern int tcp_server(uint8_t sn, uint16_t port);

因为该函数的实现是状态机,所以在while(1)中调用:

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	tcp_server(0, 8000);
}
/* USER CODE END 3 */

编译,下载到开发板中,使用串口助手查看结果:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
在PC上运行socket调试助手,建立一个tcp client连接开发板:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
TCP客户端连接之后,可以看到开发板打印出的信息:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
接着在网络调试助手中发送信息:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
可以看到,网络调试助手发送之后,收到了开发板回发的消息,在开发板上也可以看到日志:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
当TCP客户端断开链接后,查看开发板串口日志:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端

三、TCP客户端的实现

1. 实现功能

实现一个TCP客户端,可以发起TCP客户端的请求,先向服务器发送数据,然后接收服务器回发的数据之后打印。

2. TCP客户端状态机

3. 实现代码

编写demo文件tcp_client.c

#include "socket.h"
#include <stdio.h>
#include <string.h>

#define DEFAULT_PORT            6000
#define DATA_BUF_SIZE			2048
static uint8_t send_buf[DATA_BUF_SIZE];
static uint8_t recv_buf[DATA_BUF_SIZE];

int tcp_client(uint8_t sn, uint8_t *dest_ip, uint16_t dest_port)
{
    int8_t ret;
    uint16_t recv_size = 0;
    uint16_t send_size = 0;
    
    switch (getSn_SR(sn)) {
    
        case SOCK_CLOSED:
            /* open socket */
            printf("TCP client start\r\n");
            if ((ret = socket(sn, Sn_MR_TCP, DEFAULT_PORT, 0x00)) != sn) {
                printf("socket %d open fail\r\n", sn);
                return ret;
            }
            printf("socket %d open success\r\n", sn);
            break;
        
        case SOCK_INIT:
            /* connect server */
            printf("try to connect %d.%d.%d.%d:%d...\r\n", dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], dest_port);
            if ((ret = connect(sn, dest_ip, dest_port)) != SOCK_OK) {
                return ret;
            }
            break;
        case SOCK_ESTABLISHED:
            /* socket has been established */
            if(getSn_IR(sn) & Sn_IR_CON) {
                printf("%d:Connected to - %d.%d.%d.%d : %d\r\n",sn, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], dest_port);
                setSn_IR(sn,Sn_IR_CON);
            }
            
             // get the size of recv data in recv buffer
            if ((recv_size = getSn_RX_RSR(sn)) > 0) {
                if (recv_size > DATA_BUF_SIZE) {
                    recv_size = DATA_BUF_SIZE;
                }
                
                //recv data
                ret = recv(sn, recv_buf, DATA_BUF_SIZE);     
                if (ret <= 0) {
                    printf("%d:recv fail\r\n", sn);
                    return ret;
                } else {
                    // The actual received size
                    recv_size = (uint16_t)ret;
                    printf("%d:recv size:%d\r\n", sn, recv_size);
                    recv_buf[recv_size] = '\0';
                    printf("%d:recv data:[%s]\r\n", sn, recv_buf);
                }
                
                  //send data
                strcpy((char*)send_buf, "Hello, Server!");
                ret = send(sn, send_buf, strlen((char*)send_buf));
                if (ret <= 0) {
                    printf("%d:send fail\r\n", sn);

                    //close the socket
                    close(sn);

                    return ret;
                } else {
                    // The actual sent size
                    send_size = (uint16_t)ret;
                    printf("%d:send size:%d\r\n", sn, send_size);
                    //recv_buf[size] = '\0';
                    printf("%d:send data:[%s]\r\n", sn, send_buf);
                } 
            }
            break;
            
        case SOCK_CLOSE_WAIT:
            /* closing the socket */
            if ((ret = disconnect(sn)) != SOCK_OK) {
                printf("%d:disconnect fail\r\n", sn);
                return ret;
            }
            printf("%d: socket is closed\r\n", sn);
            break;
        
        default:
            break;
    }
    
    return 1;
}

在main.c中定义目标地址和目标端口:

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
uint8_t dest_ip[4] = {192, 168, 10, 156};
uint16_t dest_port = 8000;

/* USER CODE END PV */

声明测试函数:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern int tcp_client(uint8_t sn, uint8_t *dest_ip, uint16_t dest_port);

/* USER CODE END 0 */

在while(1)中循环调用:

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	tcp_client(0, dest_ip, dest_port);
}
/* USER CODE END 3 */

4. 实验结果

首先在电脑上使用网络调试助手建立一个TCP服务器,在8000端口:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
然后编译下载程序:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
在网络调试助手中发送消息,可以看到开发板收到数据后回发的消息:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端
在开发板一侧也可以看到日志:
W5500开发笔记 | 02 - 使用W5500 Socket API 建立TCP服务端、TCP客户端

上一篇:RNAseq-GO、biomaRt转换ID


下一篇:Uiautomator2自动化测试框架