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\r\n");
    }
    
    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("發送失敗\r\n");
                        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\r\n");
                    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\r\n",udp_demo_recvbuf);  
                    netbuf_delete(recvbuf);      //刪除buf
                }
                else 
                    OSTimeDlyHMSM(0,0,0,10);  //延時5ms
            }
        }
        else 
            printf("UDP綁定失敗\r\n");
    }
    else 
        printf("UDP連接創建失敗\r\n");
}

這都沒什么好說的,但是需要注意的是,最好將udp的優先級別設置的低於數據查詢任務的優先級別

工程代碼如下

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

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM