zoukankan      html  css  js  c++  java
  • LWIP_STM32_ENC28J60_NETCONN_UDP(3)

        前面移植了lwip之后只是简单地做了一个dhcp的程序,但是实际工作中经常要用来通讯,那今天就来讲一讲怎么用lwip来进行UDP通讯

        要使用数据通信首先第一步得知道lwip是怎么样保存数据的,在使用netconn数据包进行通讯的时候,netbuf是主要的数据结构,该数据结构的构成如下

    struct netbuf {
      struct pbuf *p, *ptr;
      ip_addr_t addr;
      u16_t port;
    #if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
    #if LWIP_CHECKSUM_ON_COPY
      u8_t flags;
    #endif /* LWIP_CHECKSUM_ON_COPY */
      u16_t toport_chksum;
    #if LWIP_NETBUF_RECVINFO
      ip_addr_t toaddr;
    #endif /* LWIP_NETBUF_RECVINFO */
    #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
    };

    蛮长一串,但是不用全部弄明白只需要关注这几个

    struct pbuf *p, *ptr;
    ip_addr_t addr;
    u16_t port;

      首先,系统中有一个pbuf的链表,p指向pbuf的顶端,ptr指向当前正在使用的pbuf,netbuf_next()和 netbuf_first()操作 ptr 字段。 addr 和 port 字段用来记录数据发送方的 IP 地址和端口号,netbuf_fromaddr 和 netbuf_fromport 这两个宏定义用于返回 addr 和 port 这两个字段。netbuf和 pbuf 之间的关系如图

    不管是 TCP 连接还是 UDP 连接, 接收到数据包后会将数据封装在一个 netbuf 中,然后将这个 netbuf 交给应用程序去处理。在数据发送时,根据不同的连接有不同的处理:对于 TCP 连
    接,用户只需要提供待发送数据的起始地址和长度,内核会根据实际情况将数据封装在合适大小的数据包中,并放入发送队列中,对于 UDP 来说,用户需要自行将数据封装在 netbuf 结构
    中,当发送函数被调用的时候内核直接将数据包中的数据发送出去

    操作netbuf的api主要有下面几个

    这只是数据结构,接下来是使用这些数据结构的api,主要是下面这些

    这里面用到了一个新的数据结构,叫做netconn,也就是今天我们的核心,他的构成如下

    /** A netconn descriptor */
    struct netconn {
      /** 连接类型 (TCP, UDP or RAW) */
      enum netconn_type type;
      /** 当前连接状态 */
      enum netconn_state state;
      /** 内核中与这个相关的控制块指针 */
      union {
        struct ip_pcb  *ip;
        struct tcp_pcb *tcp;
        struct udp_pcb *udp;
        struct raw_pcb *raw;
      } pcb;
      /** 这个链接最近的一个错误 */
      err_t last_err;
      /** 用于两个api上下文同步的信号量 */
      sys_sem_t op_completed;
      /**接收数据的消息邮箱 */
      sys_mbox_t recvmbox;
    #if LWIP_TCP
      /** 用于TCP服务器,连接请求的缓冲队列 */
      sys_mbox_t acceptmbox;
    #endif /* LWIP_TCP */
      /** socket描述符,用于socket类的api */
    #if LWIP_SOCKET
      int socket;
    #endif /* LWIP_SOCKET */
    #if LWIP_SO_SNDTIMEO
      /** 发送数据的超时时间 */
      s32_t send_timeout;
    #endif /* LWIP_SO_RCVTIMEO */
    #if LWIP_SO_RCVTIMEO
      /** 接收数据的超时时间 */
      int recv_timeout;
    #endif /* LWIP_SO_RCVTIMEO */
    #if LWIP_SO_RCVBUF
      /** 接收消息队列的长度 */
      int recv_bufsize;
      /** 当前接收邮箱中已经缓存的数据长度 */
      s16_t recv_avail;
    #endif /* LWIP_SO_RCVBUF */
      /** netconn内部的状态标识符 */
      u8_t flags;
    #if LWIP_TCP
      /** 当调用 netconn_write 发送数据但缓存不足的时候,数据会暂时存放在 current_msg 中,等待下
    一次数据发送, write_offset 记录下一次发送时的索引 */
      size_t write_offset;
      /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
          this temporarily stores the message.
          Also used during connect and close. */
      struct api_msg_msg *current_msg;
    #endif /* LWIP_TCP */
      /** //连接相关回调函数,实现 socket API 时使用 */
      netconn_callback callback;
    };

    与这个api相关的还有一些枚举,这里就先不说了,基本上通过这些api就能够进行网络通讯,接下来看看怎样实现udp通讯

    流程是这样的:创建一个udp链接--绑定一个udp端口--设定目的地的端口和ip,然后就能发送了,因为udp本身是无连接的,所以我们不需要等待udp连连接上在操作,具体的代码如下

    //创建UDP线程
    //返回值:0 UDP创建成功
    //        其他 UDP创建失败
    INT8U udp_demo_init(void)
    {
        INT8U res;
        OS_CPU_SR cpu_sr;
        
        OS_ENTER_CRITICAL();    //关中断
        res = OSTaskCreate(udp_thread,(void*)0,(OS_STK*)&UDP_TASK_STK[UDP_STK_SIZE-1],UDP_PRIO); //创建UDP线程
        OS_EXIT_CRITICAL();        //开中断
        
        return res;
    }

    创建任务之后任务如下

    //udp任务函数
    static void udp_thread(void *arg)
    {
        OS_CPU_SR cpu_sr;
        err_t err;
        static struct netconn *udpconn;
        static struct netbuf  *recvbuf;
        static struct netbuf  *sentbuf;
        struct ip_addr destipaddr;
        u32 data_len = 0;
        struct pbuf *q;
        
        while(dhcpstatus != 2)//等待dhcp成功
        {
            OSTimeDly(10);
            printf("wait dhcp
    ");
        }
        
        LWIP_UNUSED_ARG(arg);
        udpconn = netconn_new(NETCONN_UDP);  //创建一个UDP链接
        udpconn->recv_timeout = 10;          
        
        if(udpconn != NULL)  //创建UDP连接成功
        {
            err = netconn_bind(udpconn,IP_ADDR_ANY,UDP_DEMO_PORT); 
            IP4_ADDR(&destipaddr,192,168,1,105); //构造目的IP地址
            netconn_connect(udpconn,&destipaddr,UDP_DEMO_PORT);     //连接到远端主机
            if(err == ERR_OK)//绑定完成
            {
                while(1)
                {
                    if(keyValue == KEY_DOWN)
                    {
                        udp_flag = LWIP_SEND_DATA;
                    }
                    if((udp_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) //有数据要发送
                    {
                        sentbuf = netbuf_new();
                        netbuf_alloc(sentbuf,strlen((char *)udp_demo_sendbuf));
                        sentbuf->p->payload = (char*)udp_demo_sendbuf;       //指udp_demo_sendbuf组
                        err = netconn_send(udpconn,sentbuf);      //将netbuf中的数据发送出去
                        if(err != ERR_OK)
                        {
                            printf("发送失败
    ");
                            netbuf_delete(sentbuf);      //删除buf
                        }
                        udp_flag &= ~LWIP_SEND_DATA;    //清除数据发送标志
                        netbuf_delete(sentbuf);          //删除buf
                        keyValue = 0;
                    }    
                    
                    netconn_recv(udpconn,&recvbuf); //接收数据
                    if(recvbuf != NULL)          //接收到数据
                    { 
                        OS_ENTER_CRITICAL(); //关中断
                        printf("receive data
    ");
                        memset(udp_demo_recvbuf,0,UDP_DEMO_RX_BUFSIZE);  //数据接收缓冲区清零
                        for(q=recvbuf->p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
                        {
                            //判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于
                            //的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
                            if(q->len > (UDP_DEMO_RX_BUFSIZE-data_len)) 
                                memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_DEMO_RX_BUFSIZE-data_len));//拷贝数据
                            else 
                                memcpy(udp_demo_recvbuf+data_len,q->payload,q->len);
                            data_len += q->len;      
                            if(data_len > UDP_DEMO_RX_BUFSIZE) 
                                break; //超出TCP客户端接收数组,跳出    
                        }
                        OS_EXIT_CRITICAL();  //开中断
                        data_len=0;  //复制完成后data_len要清零。
                        //打印接收到的数据
                        printf("%s
    ",udp_demo_recvbuf);  
                        netbuf_delete(recvbuf);      //删除buf
                    }
                    else 
                        OSTimeDlyHMSM(0,0,0,10);  //延时5ms
                }
            }
            else 
                printf("UDP绑定失败
    ");
        }
        else 
            printf("UDP连接创建失败
    ");
    }

    这都没什么好说的,但是需要注意的是,最好将udp的优先级别设置的低于数据查询任务的优先级别

    工程代码如下

    http://download.csdn.net/detail/dengrengong/8599069



  • 相关阅读:
    mysql事务
    python zip dict函数
    有关mysql索引
    hash(散列函数)
    Docker使用
    Mac终端命令自动补全
    python序列化与反序列化(json与pickle)
    python txt、excel读写
    python 正则表达式
    python进行数据预处理-pandas
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4433726.html
Copyright © 2011-2022 走看看