W6100-EVB-PICO作为TCP Client 进行数据回环测试(五)

前言

上一章我们用W6100-EVB-PICO开发板通过DNS解析www.baidu.com(百度域名)成功得到其IP地址,那么本章我们将用我们的开发板作为客户端去连接服务器,并做数据回环测试:收到服务器发送的数据,并回传给服务器。

TCP是什么?什么是TCP Client? 能做什么?

TCP (Transmission Control Protocol) 是一种面向连接的、可靠的、基于字节流的传输协议,用于在计算机网络上传输数据。TCP Client是指TCP网络服务的客户端连接,主动向服务器发起连接请求并建立连接,用于实现串口数据和服务器数据的交互,保证数据的可靠交换。TCP Clent通常用于设备与服务器之间的数据交互,是最常用的联网通信方式。

TCP Client的主要作用是建立和管理与TCP服务器之间的连接,实现数据的可靠传输。通过TCP Client,设备可以向服务器发送数据并从服务器接收数据,从而实现设备与服务器之间的数据交互。

在TCP Client中,客户端程序需要指定服务器的IP地址和端口号,并使用TCP协议与服务器建立连接。一旦连接建立成功,客户端程序就可以通过数据流对象 (NetworkStream) 与服务器进行数据交互。

因此,TCP Client可以帮助设备实现与服务器之间的可靠数据交换,是设备联网通信的重要方式之一。在工业自动化、物联网、智能家居等应用中,TCP Client被广泛使用。

连接方式

使开发板和我们的电脑处于同一网段:

  • 开发板(设备)通过交叉线直连主机(PC)
  • 开发板和主机都接在路由器LAN口

测试工具

  • 网络调试工具(任意)
  • wireshark抓包工具

回环测试

1. 相关代码

如下所示,我们可以看到应用实例里面loopback_tcpc的具体实现,我们需要传入五个参数:socket端口号、收发缓存、目的IP地址、目的端口号和回环模式;函数里面用一个Switch状态机,对socket端口状态轮询并进行相应的处理,处于连接状态就判断是否收到数据,如果有就获取数据大小,并做回传处理,回传失败就关闭端口;处于关闭状态就跑tcp协议并打开端口;处于初始化状态就连接服务器;处于等待关闭状态就断开连接。

值得一提的是W6100这款以太网芯片支持IPv6,因此在回环模式上有着不同选择,相应地处理上也会根据模式分别进行处理,这里选择IPv4模式进行回环测试。

cpp 复制代码
int32_t loopback_tcpc(uint8_t sn, uint8_t* buf, uint8_t* destip, uint16_t destport, uint8_t loopback_mode)
{

    int32_t ret; // return value for SOCK_ERRORs
    datasize_t sentsize=0;
    uint8_t status,inter,addr_len;
    datasize_t received_size;
    uint8_t tmp = 0;
    uint8_t arg_tmp8;
    wiz_IPAddress destinfo;


    // Socket Status Transitions
    // Check the W6100 Socket n status register (Sn_SR, The 'Sn_SR' controlled by Sn_CR command or Packet send/recv status)
    getsockopt(sn,SO_STATUS,&status);
    switch(status)
    {
    case SOCK_ESTABLISHED :
        ctlsocket(sn,CS_GET_INTERRUPT,&inter);
        if(inter & Sn_IR_CON)	// Socket n interrupt register mask; TCP CON interrupt = connection with peer is successful
        {
            #ifdef _LOOPBACK_DEBUG_
                printf("%d:Connected to - %d.%d.%d.%d : %d\r\n",sn, destip[0], destip[1], destip[2], destip[3], destport);
            #endif
            arg_tmp8 = Sn_IR_CON;
            ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);// this interrupt should be write the bit cleared to '1'
        }

        //
        // Data Transaction Parts; Handle the [data receive and send] process
        //
        getsockopt(sn, SO_RECVBUF, &received_size);

        if(received_size > 0) // Sn_RX_RSR: Socket n Received Size Register, Receiving data length
        {
            if(received_size > DATA_BUF_SIZE) received_size = DATA_BUF_SIZE; // DATA_BUF_SIZE means user defined buffer size (array)
            ret = recv(sn, buf, received_size); // Data Receive process (H/W Rx socket buffer -> User's buffer)

            if(ret <= 0) return ret; // If the received data length <= 0, receive failed and process end
            received_size = (uint16_t) ret;
            sentsize = 0;

            // Data sentsize control
            while(received_size != sentsize)
            {
                ret = send(sn, buf+sentsize, received_size-sentsize); // Data send process (User's buffer -> Destination through H/W Tx socket buffer)
                if(ret < 0) // Send Error occurred (sent data length < 0)
                {
                    close(sn); // socket close
                    return ret;
                }
                sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
        }
        //
        break;

    case SOCK_CLOSE_WAIT :
        #ifdef _LOOPBACK_DEBUG_
            printf("%d:CloseWait\r\n",sn);
        #endif
        getsockopt(sn, SO_RECVBUF, &received_size);

        if((received_size = getSn_RX_RSR(sn)) > 0) // Sn_RX_RSR: Socket n Received Size Register, Receiving data length
        {
            if(received_size > DATA_BUF_SIZE) received_size = DATA_BUF_SIZE; // DATA_BUF_SIZE means user defined buffer size (array)
            ret = recv(sn, buf, received_size); // Data Receive process (H/W Rx socket buffer -> User's buffer)

            if(ret <= 0) return ret; // If the received data length <= 0, receive failed and process end
            received_size = (uint16_t) ret;
            sentsize = 0;

            // Data sentsize control
            while(received_size != sentsize)
            {
                ret = send(sn, buf+sentsize, received_size-sentsize); // Data send process (User's buffer -> Destination through H/W Tx socket buffer)
                if(ret < 0) // Send Error occurred (sent data length < 0)
                {
                    close(sn); // socket close
                    return ret;
                }
                sentsize += ret; // Don't care SOCKERR_BUSY, because it is zero.
            }
        }
        if((ret=disconnect(sn)) != SOCK_OK) return ret;
            #ifdef _LOOPBACK_DEBUG_
                printf("%d:Socket Closed\r\n", sn);
            #endif
        break;

    case SOCK_INIT :
        #ifdef _LOOPBACK_DEBUG_
            if(loopback_mode == AS_IPV4)
                printf("%d:Try to connect to the %d.%d.%d.%d, %d\r\n", sn, destip[0], destip[1], destip[2], destip[3], destport);
            else if(loopback_mode == AS_IPV6)
            {
                printf("%d:Try to connect to the %04X:%04X", sn, ((uint16_t)destip[0] << 8) | ((uint16_t)destip[1]),
                    ((uint16_t)destip[2] << 8) | ((uint16_t)destip[3]));
                printf(":%04X:%04X", ((uint16_t)destip[4] << 8) | ((uint16_t)destip[5]),
                    ((uint16_t)destip[6] << 8) | ((uint16_t)destip[7]));
                printf(":%04X:%04X", ((uint16_t)destip[8] << 8) | ((uint16_t)destip[9]),
                    ((uint16_t)destip[10] << 8) | ((uint16_t)destip[11]));
                printf(":%04X:%04X,", ((uint16_t)destip[12] << 8) | ((uint16_t)destip[13]),
                    ((uint16_t)destip[14] << 8) | ((uint16_t)destip[15]));
                printf("%d\r\n", destport);
            }
        #endif

        if(loopback_mode == AS_IPV4)
          ret = connect(sn, destip, destport, 4); /* Try to connect to TCP server(Socket, DestIP, DestPort) */
        else if(loopback_mode == AS_IPV6)
          ret = connect(sn, destip, destport, 16); /* Try to connect to TCP server(Socket, DestIP, DestPort) */

        printf("SOCK Status: %d\r\n", ret);

        if( ret != SOCK_OK) return ret;	//	Try to TCP connect to the TCP server (destination)
        break;

    case SOCK_CLOSED:
        switch(loopback_mode)
        {
        case AS_IPV4:
            tmp = socket(sn, Sn_MR_TCP4, any_port++, SOCK_IO_NONBLOCK);
            break;
        case AS_IPV6:
            tmp = socket(sn, Sn_MR_TCP6, any_port++, SOCK_IO_NONBLOCK);
            break;
        case AS_IPDUAL:
            tmp = socket(sn, Sn_MR_TCPD, any_port++, SOCK_IO_NONBLOCK);
            break;
        default:
            break;
        }

        if(tmp != sn){    /* reinitialize the socket */
            #ifdef _LOOPBACK_DEBUG_
                printf("%d : Fail to create socket.\r\n",sn);
            #endif
            return SOCKERR_SOCKNUM;
        }
        printf("%d:Socket opened[%d]\r\n",sn, getSn_SR(sn));
        sock_state[sn] = 1;

        break;
    default:
        break;
    }
    return 1;
}

主函数就比较简单,在此之前我们先声明socket端口号和所用最大的缓存大小,不做分片处理默认为2KB;然后初始化网络信息、目标IP地址和目标端口,最后在while循环里调用loopback_tcpc并传入相应参数即可。

注意:这里的目的IP地址设置为我们的电脑IP地址,因为我们要让电脑端作为服务器,使用网络调试助手进行数据回环测试;另外目标端口选择尽量避免使用特殊端口,这里使用8080

cpp 复制代码
#define SOCKET_ID 0
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)

void network_init(void);

wiz_NetInfo net_info = {
    .mac = {0x00, 0x08, 0xdc, 0x16, 0xed, 0x2e},
    .ip = {192, 168, 1, 10},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 1, 1},
    .dns = {8, 8, 8, 8},
    .ipmode = NETINFO_STATIC_V4};
wiz_NetInfo get_info;
static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0,};

static uint8_t des_ip[4] = {192, 168, 1, 2};
static uint16_t des_port = 8080;

int main()                                                          
{   
    stdio_init_all();
    sleep_ms(2000);
    network_init();

    while(true)
    {
        loopback_tcpc(SOCKET_ID, ethernet_buf, des_ip, des_port, AS_IPV4);
        sleep_ms(500);
    }
    

}

void network_init(void)
{
    uint8_t temp;
    wizchip_initialize();
     printf("W6100 dns test example.\r\n");
    sleep_ms(2000);
     /* Determine the network lock register status */
    if(!ctlwizchip(SYS_NET_LOCK, &temp))
    {   
        printf("unlock.\n");
        NETUNLOCK();
    }
    wizchip_setnetinfo(&net_info);
    print_net_info(&get_info);
    sleep_ms(2000);   
}

2.测试现象

我们编译烧录后,打开串行监视器,可以看到,配置相关信息后,尝试连接我们初始化设置的目的IP(电脑IP),然后我们在电脑上打开网络调试助手,选择tcp服务器模式,IP选择电脑的本机IP(一般默认即为电脑IP),端口号写8080(跟我们在开发板配置的信息一致,不然监听不到),配置完成打开后,可以看到客户端上线提示,尝试发送数据,可以看到成功回传。

我们也可以在打开wireshark抓包工具,输入命令<ip.addr == 192.168.1.10 and tcp>过滤数据包(IP地址改成自己电脑的,也即开发板设置的目标IP地址);我这里先关闭网络调试助手,然后又打开,接着发送0~9十个阿拉伯数字,可以通过抓包工具十分清楚明了的看到具体交互过程,如下图所示。

相关链接:

本章相应例程链接https://gitee.com/wiznet-hk/example-of-w6100-evb-pico.gitwireshark抓包工具下载链接https://www.wireshark.org/download.html

相关推荐
WIZnet 中国社区官方博客20 天前
第五章 GPIO示例
程序设计·wiznet·高性能以太网单片机·w55mh32·单片机外设·寄存器描述·gpio介绍
WIZnet 中国社区官方博客21 天前
【第二十三章 IAP】
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·iap简介·iap程序设计
WIZnet 中国社区官方博客23 天前
第二章 开发板与芯片介绍
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·系统框架·开发板介绍·硬件资源
WIZnet 中国社区官方博客23 天前
第十三章 RTC 实时时钟
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·rtc简介·寄存器描述
WIZnet 中国社区官方博客24 天前
第二十章 BKP
wiznet·高性能以太网单片机·w55mh32·单片机外设·bkp简介·bkp特性·rtc校准
WIZnet 中国社区官方博客24 天前
第十六章 I2C
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·单片机外设·i2c通讯·i2c主从模式
WIZnet1 个月前
第二十八章 RTC——实时时钟
嵌入式硬件·时间戳·wiznet·高性能以太网单片机·w55mh32·rtc实时时钟·时钟分频
WIZnet1 个月前
第十九章 ADC——电压采集
嵌入式硬件·wiznet·高性能以太网单片机·w55mh32·adc电压采集·adc模数转换·嵌入式学习教程
邓校长的编程课堂6 个月前
基于树莓派Pico和声音传感器实现声控风扇的技术分享
物联网·嵌入式开发·树莓派pico·编程入门·c++编程·声音传感器·c++趣味编程
邓校长的编程课堂7 个月前
c++编程&玩转物联网:使用芯片控制8个LED实现流水灯技术分享
c++·物联网·嵌入式开发·树莓派pico·led流水灯·位操作·c++编程实践