0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

有线网络通信实验2之UDP协议

汽车电子技术 来源:滑小稽笔记 作者:电子技术园地 2023-03-01 14:29 次阅读

25.1 UDP协议概述

UDP协议是TCP/IP协议栈中传输层协议,是一个简单的面向数据报的协议,在传输层中还有一个TCP协议,UDP不提供数据包分组,组装,无法对数据包进行排序,当报文发送出去之后无法知道是否安全,完整的到达,但是由于UDP不属于连接性协议,所以消耗资源小,处理速度快,通常用于音频视频和普通数据传输中,UDP数据包结构如下图所示。

图片

端口号表示发送和接收进程,UDP使用端口号为不同的应用保留各自的数据传输通道,UDP和TCP都是采用端口号的形式对同一时刻多个应用同时发送和接受数据,而数据接收方则通过目标端口接受数据,有的网络只能使用预先预留或注册的静态端口,而一些网络可以使用没有被注册的动态端口,由于UDP包头使用两个字节存放端口号,所以端口的有效范围0~65535,一般,大于49151的端口号都代表动态端口。

数据包的长度指的是包括包头和数据部分在内的总字节数,由于包头的长度固定,所以这个区域主要用于计算可变长度的数据部分,数据包的最大长度根据操作环境选择,理论上说,包括包头在内的数据报文最大长度为65535字节。

UDP通过包头中的校验和来保证数据的完整性,校验和首先在数据发送方通过特殊的算法计算出,传递到接收方之后,需要重新计算,如果某个数据在输出过程中被篡改或某种原因损坏,那么发送方和接收方的校验和就会不一致,因此,UDP协议具有检测报文是否出错的能力。

udp.c和udp.h这两个文件就是负责实现UDP传输协议的文件,与UDP报文处理有关的函数之间的关系如下图所示。

图片

LWIP协议中API编程方式是基于回调机制的,在我们初始化应用的时候必须为内核中不同的事件注册给出对应的回调函数,当对应的事件发生后这些回调函数就会被调用,udp.c中常用的API功能函数如下表所示。

API函数 函数功能
udp_new 新建一个UDP的PCB
udp_remove 将一个PCB控制块从链表中删除,并释放这个控制块的内存
udp_bind 为UDP的PCN控制块绑定一个本地IP地址和端口号
udp_connect 连接到指定IP地址主机的指定端口上
udp_disconnent 断开连接,将控制块设置为非连接状态
udp_send 通过一个PCB控制块发送数据
udp_recv 需要创建一个回调函数,当接受到数据的时候被调用

25.2 应用编写

在LWIP/app/udp_demo目录下创建udp_demo.c和udp_demo.h文件。

25.2.1 udp_demo.c代码编写

#include "udp_demo.h" 
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "malloc.h"
#include "string.h"
#include "comm.h"
#include "lwip/pbuf.h"
#include "lwip/udp.h"
#include "lwip/tcp.h"
//UDP 测试全局状态标记变量
//bit6:数据接收状态
//bit5:连接状态
u8 udp_demo_flag;
//设置远端IP地址
void udp_demo_set_remoteip()
{
  u8 *tbuf ;
  LCD_Clear( WHITE ) ;
  POINT_COLOR = RED ;
  tbuf = mymalloc( SRAMIN, 100 ) ;                                  //申请内存
  if( tbuf==NULL )
    return ;
  //前三个IP保持和DHCP得到的IP一致
  lwipdev.remoteip[ 0 ] = lwipdev.ip[ 0 ] ;
  lwipdev.remoteip[ 1 ] = lwipdev.ip[ 1 ] ;
  lwipdev.remoteip[ 2 ] = lwipdev.ip[ 2 ] ;
  lwipdev.remoteip[ 3 ] = 113 ;
  sprintf( ( char* )tbuf, "Remote IP:%d.%d.%d.", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2] ) ;
  LCD_ShowString( 30, 150, tbuf ) ;                //远端IP
  myfree( SRAMIN, tbuf ) ;
}
// UDP接收回调函数
u8 udp_demo_recvbuf[ UDP_DEMO_RX_BUFSIZE ] ;            //UDP接收数据缓冲区
void udp_demo_recv( void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port )
{
  u32 data_len=0 ;
  struct pbuf *q ;
  //接收到不为空的数据时
  if( p!=NULL )
  {
    memset( udp_demo_recvbuf, 0, UDP_DEMO_RX_BUFSIZE ) ;    //数据接收缓冲区清零
    //遍历完整个pbuf链表
    for( q=p; q!=NULL; q=q->next )
    {
      //拷贝数据
      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 ;
      //超出TCP客户端接收数组,跳出
      if( data_len>UDP_DEMO_RX_BUFSIZE )
        break ;  
    }
    upcb->remote_ip = *addr ;                  //记录远程主机的IP地址
    upcb->remote_port = port ;                //记录远程主机的端口号
    lwipdev.remoteip[ 0 ] = upcb->remote_ip.addr&0xFF ;      //IADDR4
    lwipdev.remoteip[ 1 ] = ( upcb->remote_ip.addr>>8 )&0xFF ;  //IADDR3
    lwipdev.remoteip[ 2 ] = ( upcb->remote_ip.addr>>16 )&0xFF ;  //IADDR2
    lwipdev.remoteip[ 3 ] = ( upcb->remote_ip.addr>>24 )&0xFF ;  //IADDR1 
    udp_demo_flag |= 1<<6 ;                  //标记接收到数据了
    pbuf_free( p ) ;                      //释放内存
  }
  else
  {
    udp_disconnect( upcb ) ;
    LCD_Clear( WHITE ) ;                    //清屏
    udp_demo_flag &= ~( 1<<5 ) ;                //标记连接断开
  }
}
// UDP服务器发送数据
const u8 *tcp_demo_sendbuf="STM32F103 UDP send data\\r\\n";
void udp_demo_senddata( struct udp_pcb *upcb )
{
  struct pbuf *ptr ;
  ptr = pbuf_alloc( PBUF_TRANSPORT, strlen( ( char* )tcp_demo_sendbuf ), PBUF_POOL ) ;//申请内存
  if( ptr )
  {
    ptr->payload = ( void* )tcp_demo_sendbuf ;
    udp_send( upcb, ptr ) ;                    //udp发送数据 
    pbuf_free( ptr ) ;                                        //释放内存
  }
}
//关闭UDP连接
void udp_demo_connection_close( struct udp_pcb *upcb )
{
  udp_disconnect( upcb ) ;
  udp_remove( upcb ) ;                      //断开UDP连接 
  udp_demo_flag &= ~( 1<<5 ) ;                  //标记连接断开
  LCD_Clear( WHITE ) ;                      //清屏
}
// UDP测试
void udp_demo_test()
{
   err_t err ;
  struct udp_pcb *udppcb ;                    //定义一个TCP服务器控制块
  struct ip_addr rmtipaddr ;                                      //远端ip地址
  u8 *tbuf ;
  u8 res=0 ;
  udp_demo_set_remoteip() ;                    //先选择IP
  LCD_Clear( WHITE ) ;                      //清屏
  tbuf = mymalloc( SRAMIN, 200 ) ;                //申请内存
  //内存申请失败了,直接退出
  if( tbuf==NULL )
    return ;
  sprintf( ( char* )tbuf, "Local IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  LCD_ShowString( 30, 150, tbuf ) ;                //服务器IP
  sprintf( ( char* )tbuf, "Remote IP:%d.%d.%d.%d", lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
  LCD_ShowString( 30, 170, tbuf ) ;                //远端IP
  sprintf( ( char* )tbuf, "Remote Port:%d", UDP_DEMO_PORT ) ;
  LCD_ShowString( 30, 190, tbuf ) ;                //客户端端口号
  LCD_ShowString( 30, 210, "STATUS:Disconnected" ) ;
  udppcb = udp_new() ;
  //创建成功
  if( udppcb )
  { 
    IP4_ADDR( &rmtipaddr, lwipdev.remoteip[0], lwipdev.remoteip[1], lwipdev.remoteip[2], lwipdev.remoteip[3] ) ;
    err = udp_connect( udppcb, &rmtipaddr, UDP_DEMO_PORT ) ;  //UDP客户端连接到指定IP地址和端口
    if( err==ERR_OK )
    {
      err = udp_bind( udppcb, IP_ADDR_ANY, UDP_DEMO_PORT ) ; //绑定本地IP地址与端口号
      //绑定完成
      if( err==ERR_OK )
      {
        udp_recv( udppcb, udp_demo_recv, NULL ) ;      //注册接收回调函数
        LCD_ShowString( 30, 210, "STATUS:Connected   " ) ;  //标记连接上了
        udp_demo_flag |= 1<<5 ;              //标记已经连接上
        LCD_ShowString( 30, 230, "Receive Data:" ) ;      //提示消息
      }
      else
        res = 1 ;
    }
    else
      res = 1 ;
  }
  else
    res = 1 ;
  while( res==0 )
  {
    //是否收到数据
    if( udp_demo_flag&1<<6 )
    {
      LCD_ShowString( 30, 250, udp_demo_recvbuf ) ;      //显示接收到的数据
      udp_demo_senddata( udppcb ) ;              //发送数据
      udp_demo_flag &= ~( 1<<6 ) ;              //标记数据已经被处理了
    } 
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
  }
  udp_demo_connection_close( udppcb ) ;
  myfree( SRAMIN, tbuf ) ;
}

25.2.2 udp_demo.h代码编写

#ifndef _UDP_DEMO_H_
#define _UDP_DEMO_H_
#include "sys.h"
#define UDP_DEMO_RX_BUFSIZE  2000              //定义udp最大接收数据长度 
#define UDP_DEMO_PORT      8089              //定义udp连接的端口 
void udp_demo_test( void ) ;                    //UDP测试
#endif

25.2.3 主函数代码编写

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "tim.h"
#include "lcd.h"
#include "malloc.h"
#include "dm9000.h"
#include "lwip/netif.h"
#include "comm.h"
#include "lwipopts.h"
#include "udp_demo.h"
int main()
{
  u8 buf[ 30 ];
   STM32_Clock_Init( 9 ) ;                        //系统时钟设置
  SysTick_Init( 72 ) ;                          //延时初始化
  USART1_Init( 72, 115200 ) ;                      //串口初始化为115200
  LCD_Init() ;                            //初始化LCD
  TIM3_Init( 1000, 719 ) ;                        //定时器3频率为100hz
  my_mem_init( SRAMIN ) ;                      //初始化内部内存池
  while( lwip_comm_init() ) ;                      //lwip初始化
  //等待DHCP获取成功/超时溢出
  while( ( lwipdev.dhcpstatus!=2 )&&( lwipdev.dhcpstatus!=0xFF ) )
  {
    lwip_periodic_handle() ;                    //LWIP内核需要定时处理的函数
    lwip_pkt_handle() ;
  }
  POINT_COLOR=RED;
  LCD_ShowString( 30, 110, "LWIP Init Successed" ) ;
  //打印动态IP地址
  if( lwipdev.dhcpstatus==2 )
    sprintf( ( char* )buf, "DHCP IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  //打印静态IP地址
  else
    sprintf( ( char* )buf, "Static IP:%d.%d.%d.%d", lwipdev.ip[0], lwipdev.ip[1], lwipdev.ip[2], lwipdev.ip[3] ) ;
  LCD_ShowString( 30, 130, buf ) ; 
  //得到网速
  if( ( DM9000_Get_SpeedAndDuplex()&0x02 )==0x02 )
    LCD_ShowString( 30, 150, "Ethernet Speed:10M" ) ;
  else
    LCD_ShowString( 30, 150, "Ethernet Speed:100M" ) ;
   while( 1 )
  {
    udp_demo_test();
    lwip_periodic_handle() ;
    lwip_pkt_handle() ;
    delay_ms( 2 ) ;
  }
}

25.3 实验结果

图片

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据传输
    +关注

    关注

    9

    文章

    1810

    浏览量

    64440
  • UDP协议
    +关注

    关注

    0

    文章

    69

    浏览量

    12676
  • 传输层协议
    +关注

    关注

    0

    文章

    6

    浏览量

    1252
收藏 人收藏

    评论

    相关推荐

    #硬声创作季 #网络通信 网络通信原理-4.5 UDP协议解析1

    UDP协议网络通信
    水管工
    发布于 :2022年11月30日 15:25:54

    #硬声创作季 #网络通信 网络通信原理-4.5 UDP协议解析2

    UDP协议网络通信
    水管工
    发布于 :2022年11月30日 15:26:13

    #硬声创作季 #网络通信 网络通信原理-10.3 TCP与UDP协议03

    UDP协议网络通信
    水管工
    发布于 :2022年11月30日 17:40:44

    #硬声创作季 #网络通信 网络通信原理-10.4 TCP与UDP协议04-2

    UDP协议网络通信
    水管工
    发布于 :2022年11月30日 17:41:26

    求labview和Linux网络通信实

    求labview和Linux网络通信实
    发表于 10-13 12:48

    请问谁有UDP协议网络通信例程?

    UDP协议网络通信例程基于enc28j60
    发表于 08-09 03:49

    为什么网络通信实验用TCP客户端模式时连接不上?

    战舰V3上的实验50 网络通信实验,当用TCP服务器模式时,通信正常,用TCP客户端模式时连接不上,为何?是否有更新的网络通信实验代码?请提供。
    发表于 08-16 04:35

    如何才能让网络通信实验支持10个UDP链接?

    大家好,我在使用探索者STM32F4开发板,寄存器例程,实验55 网络通信实验。我修改了例程,想创建10个UDP端口监听,udppcb=udp_new()这里最多只能创建5个,5个之后
    发表于 08-27 22:48

    为什么网络通信实验网络助手上接受到的先是456个字节?

    网络通信实验里将ARMF407配置成sever,通过电脑上网络助手接受ARM发送到数据,将tcp_sever_sendbuf[1000]改成一个1000的大数组并赋值,但是在网络助手上接受到的先是456个字节然后再是544个字
    发表于 09-02 02:54

    请问谁有F407网络通信实验的视频吗?

    请问有没有F407网络通信实验的视频啊,能方便给个链接或者地址吗,谢谢了,急用嘞
    发表于 09-23 01:49

    如何把stm32f407网络通信实验调通?

    最近在做网络通信实验,没有调通,有没有大神指点一下
    发表于 10-30 04:35

    请问探索者网络通信实验的控制网页该怎么写呢?

    原子哥虽然有比较详细的讲解了网络通信实验,但是对于那个控制网页怎么写的却没有过多的涉及,请问有谁会写这种控制网页的,诚心求教!
    发表于 11-06 04:35

    基于原子STM32F4的摄像头与网络通信实验

    网络通信C/S方式将STM32摄像头拍取的照片传输到电脑端在PC端开发可视化界面接受摄像头数据并更新显示2.实施步骤:1.参考STM32原子摄像头实验网络通信实验例程,主要需要了解以
    发表于 08-03 06:04

    基于UDP协议网络通信应用程序

    基于UDP协议网络通信应用程序(UDP-Socket)前两篇文章介绍了基于TCP/IP协议网络通信
    发表于 11-05 08:29

    嵌入式Linux应用程序开发-(9)UDP网络通信应用程序(UDP-Socket)

    基于UDP协议网络通信应用程序(UDP-Socket)前两篇文章介绍了基于TCP/IP协议网络通信
    发表于 11-02 12:21 35次下载
    嵌入式Linux应用程序开发-(9)<b class='flag-5'>UDP</b><b class='flag-5'>网络通信</b>应用程序(<b class='flag-5'>UDP</b>-Socket)