打印
[MM32软件]

(分享)基于MM32F3270 以太网 Client使用

[复制链接]
3642|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
七毛钱|  楼主 | 2021-10-20 09:30 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
前面介绍了基于Socket方式的以太网通讯,接下来给大家介绍基于TCP包的通讯。内容分为基于MM32F3270以太网Client的使用与基于MM32F3270以太网Server的使用。
首先,对TCP有个简单的介绍:
TCP是一种面向连接的、可靠的、基于字节流的传输通信协议。即客户端和服务器之间在交换数据之前会先建立一个TCP连接,才能相互传输数据。并且提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
TCP优点:可靠、稳定,TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。
TCP的缺点:慢,效率低,占用系统资源高,易被攻击,TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。由于TCP存在确认机制和三次握手机制,这些是导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

使用特权

评论回复
沙发
七毛钱|  楼主 | 2021-10-20 09:30 | 只看该作者
接下来,介绍Client 的使用实现:

Demo使用MB-039开发板,在工程中使用LwIP+FreeRTOS,实验展示如何制作一个Client端,并发送数据,实验使用到的硬件如下:
如图是MB-039(完整原理图可以通过MM32官网下载)的ETH部分。


使用特权

评论回复
板凳
七毛钱|  楼主 | 2021-10-20 09:31 | 只看该作者
各个信号引脚对应如下:



使用特权

评论回复
地板
七毛钱|  楼主 | 2021-10-20 09:32 | 只看该作者
                                                                                            在进行Client实验前,我们先了解需要使用到的API:

1)netconn_new ()

2)netconn_connect ()

3)netconn_write ()



每一个函数实现的功能:

01 netconn_new ()

netconn_new的功能为创建一个新的连接结构,结构类型可以为TCP/UDP其源码如下:

struct netconn*

netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)

{

    struct netconn* conn;

    API_MSG_VAR_DECLARE(msg);

    API_MSG_VAR_ALLOC_RETURN_NULL(msg);



    conn = netconn_alloc(t, callback);

    IF (conn != NULL) {

        err_t err;



        API_MSG_VAR_REF(msg).msg.n.proto = proto;

        API_MSG_VAR_REF(msg).conn = conn;

        err = netconn_apimsg(lwip_netconn_do_newconn, &API_MSG_VAR_REF(msg));

        if (err != ERR_OK) {

            LWIP_ASSERT("freeing conn without freeing PCB", conn->pcb.tcp == NULL);

            LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));

#if LWIP_TCP

            LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));

#endif /* LWIP_TCP */

#if !LWIP_NETCONN_SEM_PER_THREAD

            LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));

            sys_sem_free(&conn->op_completed);

#endif /* !LWIP_NETCONN_SEM_PER_THREAD */

            sys_mbox_free(&conn->recvmbox);

            memp_free(MEMP_NETCONN, conn);

            API_MSG_VAR_FREE(msg);

            return NULL;

        }

    }

    API_MSG_VAR_FREE(msg);

    return conn;

}

从源码中可以看出,其功能为申请并初始化一个netconn结构体,同时在netconn_alloc函数中为conn变量创建一个接收邮箱(recvmbox),和一个信号量(conn->op_completed)。内存申请成功后使用netconn_apimsg函数构建一个消息,使用OS的系统邮箱发送给内核,请求以太网协议栈去执行lwip_netconn_do_newconn()函数,在执行时使用conn->op_completed进行信号量同步,任务处理完成后,释放一个信号量表示任务完成。

02 netconn_connect ()

netconn_connect作用为建立连接,在调用时将服务器端IP地址、端口号和本地的netconn结构绑定,源码如下:

err_t  netconn_connect(struct netconn* conn, const ip_addr_t* addr, u16_t port)

{

    API_MSG_VAR_DECLARE(msg);

    err_t err;



    LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);



#if LWIP_ipv4

    /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */

    if (addr == NULL) {

        addr = IP4_ADDR_ANY;

    }

#endif /* LWIP_IPV4 */



    API_MSG_VAR_ALLOC(msg);

    API_MSG_VAR_REF(msg).conn = conn;

    API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr);

    API_MSG_VAR_REF(msg).msg.bc.port = port;

    err = netconn_apimsg(lwip_netconn_do_connect, &API_MSG_VAR_REF(msg));

    API_MSG_VAR_FREE(msg);



    return err;

}


使用特权

评论回复
5
七毛钱|  楼主 | 2021-10-20 09:33 | 只看该作者
从源码中可以看出,其功能为使用netconn_apimsg创建一个消息,通过执行lwip_netconn_do_connect进行信号量的同步,将addr、port与conn进行绑定。

03 netconn_write ()

netconn_write()为处于稳定状态的TCP协议发送数据。TCP协议数据以数据流的方式传递,因此只需要知道地址、长度及需要发送的数据即可,其实际函数为netconn_write_vectors_partly(源码较长,就不贴出来了)。重点关注一下官方API文档对于apiflagS参数的介绍:

* @param apiflags combination of following flags :

* - NETCONN_COPY: data will be copied into memory belonging to the stack

* - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent

* - NETCONN_DONTBLOCK: only write the data if all data can be written at onceapiflags的值为NETCONN_COPY时,dataptr指针指向的数据将会被拷贝到为这些数据分配的内部缓冲区,在调用本函数之后可以直接对这些数据进行修改而不会影响数据,但是拷贝的过程是需要消耗系统资源的,CPU需要参与数据的拷贝,而且还会占用新的内存空间。apiflags值为NETCONN_NOCOPY时,数据不会被拷贝而是直接使用dataptr指针来引用。但是这些数据在函数调用后不能立即被修改,因为这些数据可能会被放在当前TCP连接的重传队列中,以防对方未收到数据进行重传,而这段时间是不确定的。但是如果用户需要发送的数据在ROM中(静态数据),这样子就无需拷贝数据,直接引用数据即可。apiflags值为NETCONN_MORE时,那么接收端在组装这些TCP报文段的时候,会将报文段首部的PSH标志置一,这些数据完成组装的时候,将会被立即递交给上层应用。apiflags值为NETCONN_DONTBLOCK时,表示在内核发送缓冲区满的时候,再调用netconn_write()函数将不会被阻塞,而是会直接返回一个错误代码ERR_VAL告诉应用程序发送数据失败,应用程序可以自行处理这些数据,在适当的时候进行重传操作。apiflags值为NETCONN_NOAUTORCVD时,表示在TCP协议接收到数据的时候,调用netconn_recv_data_tcp()函数的时候不会去更新接收窗口,只能由用户自己调用netconn_tcp_recvd()函数完成接收窗口的更新操作。


使用特权

评论回复
6
七毛钱|  楼主 | 2021-10-20 09:33 | 只看该作者
                                                              了解了以上3个API,我们开始创建Client工程:static void client(void* thread_param)

{

    struct netconn* conn;

    int ret;

    ip4_addr_t ipaddr;

    uint8_t send_buf[] = "This is MM32F3270 TCP Client Demo\n";         //(1)

    while(1) {

        conn = netconn_new(NETCONN_TCP);                       //(2)

        if (conn == NULL) {                                       // (3)

            printf("create conn faiLED!\n");

            vTaskDelay(10);

            continue;

        }

        IP4_ADDR(&ipaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_Addr2, DEST_IP_Addr3);                                           // (4)

        ret = netconn_connect(conn, &ipaddr, DEST_PORT);      // (5)

        if (ret == -1) {

            printf("Connect failed!\n");

            netconn_close(conn);

            vTaskDelay(10);

            continue;

        }

        while (1) {

            ret = netconn_write(conn, send_buf, sizeof(send_buf), 0);  // (6)

            vTaskDelay(1000);

        }

    }



}

1)将需要发送的数据装填进send_buf中

2)申请一个内存区域,类型为TCP

3)如果conn为空表示申请内存失败

4)将地址赋值给ipaddr

5)创建连接,如果失败则删除conn

6)执行数据发送


使用特权

评论回复
7
七毛钱|  楼主 | 2021-10-20 09:34 | 只看该作者
到这里已经完成了工程的创建,但是还有一步比较重要的,配置我们的IP,将数据发送给服务器端,则需要知道服务器的地址。打开命令行窗口输入:ipconfig

PC地址为:192.168.105.34,在sys_arch.h文件中对DEST_IP_ADDR0 、DEST_IP_ADDR1、DEST_IP_ADDR2、DEST_IP_ADDR3进行修改,DEST_PORT随意修改,值得注意的同一个设备是如果创建多个网卡,PORT成不同的值即可,后面我们会进行这类实验,设备IP需要设置在同一个网段内通信才能进行IP_ADDR0、IP_ADDR1、IP_ADDR2,需要与PC地址保持一致,IP_ADDR3可以随意设置(和PC地址不一致即可)。#define DEST_IP_ADDR0               192
#define DEST_IP_ADDR1               168
#define DEST_IP_ADDR2               105
#define DEST_IP_ADDR3               34

#define DEST_PORT                  5001

#define IP_ADDR0                    192
#define IP_ADDR1                    168
#define IP_ADDR2                    105
#define IP_ADDR3                    137

使用特权

评论回复
8
七毛钱|  楼主 | 2021-10-20 09:35 | 只看该作者
将程序下载入开发板中,使用SSCOM工具进行如下设置:


点击侦听:

可以正常侦听并接收到数据,表明实验成功。实验程序请登录我们的官网(https://www.mindmotion.com.cn/download.aspx?cid=2542&page=2)下载MM32F3270 SDK,工程路径如下:
~\MM32F3270_Lib_Samples_V0.90\Demo_app\Ethernet_Demo\ETH_RTOS\Freertos_Client

下章的题目为《基于MM32F3270 以太网 Server使用》。


使用特权

评论回复
9
豌豆爹| | 2021-10-20 15:59 | 只看该作者
不错的知识贴学习学习

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

342

主题

2359

帖子

4

粉丝