前面移植了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*),(OS_STK*)&UDP_TASK_STK[UDP_STK_SIZE-],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 = ;
struct pbuf *q; while(dhcpstatus != )//等待dhcp成功
{
OSTimeDly();
printf("wait dhcp\r\n");
} LWIP_UNUSED_ARG(arg);
udpconn = netconn_new(NETCONN_UDP); //创建一个UDP链接
udpconn->recv_timeout = ; if(udpconn != NULL) //创建UDP连接成功
{
err = netconn_bind(udpconn,IP_ADDR_ANY,UDP_DEMO_PORT);
IP4_ADDR(&destipaddr,,,,); //构造目的IP地址
netconn_connect(udpconn,&destipaddr,UDP_DEMO_PORT); //连接到远端主机
if(err == ERR_OK)//绑定完成
{
while()
{
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 = ;
} netconn_recv(udpconn,&recvbuf); //接收数据
if(recvbuf != NULL) //接收到数据
{
OS_ENTER_CRITICAL(); //关中断
printf("receive data\r\n");
memset(udp_demo_recvbuf,,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=; //复制完成后data_len要清零。
//打印接收到的数据
printf("%s\r\n",udp_demo_recvbuf);
netbuf_delete(recvbuf); //删除buf
}
else
OSTimeDlyHMSM(,,,); //延时5ms
}
}
else
printf("UDP绑定失败\r\n");
}
else
printf("UDP连接创建失败\r\n");
}

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

工程代码如下

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

LWIP_STM32_ENC28J60_NETCONN_UDP(3)的更多相关文章

  1. stm32 学习参考(转)

    源:stm32 学习参考 单片机裸机下写一个自己的shell调试器     LWIP_STM32_ENC28J60_NETCONN_TCP_SERVICER(5)     LWIP_STM32_ENC ...

  2. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  3. Angular2学习笔记(1)

    Angular2学习笔记(1) 1. 写在前面 之前基于Electron写过一个Markdown编辑器.就其功能而言,主要功能已经实现,一些小的不影响使用的功能由于时间关系还没有完成:但就代码而言,之 ...

  4. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  5. ABP入门系列(1)——学习Abp框架之实操演练

    作为.Net工地搬砖长工一名,一直致力于挖坑(Bug)填坑(Debug),但技术却不见长进.也曾热情于新技术的学习,憧憬过成为技术大拿.从前端到后端,从bootstrap到javascript,从py ...

  6. Online Judge(OJ)搭建(第一版)

    搭建 OJ 需要的知识(重要性排序): Java SE(Basic Knowledge, String, FileWriter, JavaCompiler, URLClassLoader, Secur ...

  7. 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑

    阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...

  8. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

  9. 【.net 深呼吸】细说CodeDom(5):类型成员

    前文中,老周已经厚着脸皮介绍了类型的声明,类型里面包含的自然就是类型成员了,故,顺着这个思路,今天咱们就了解一下如何向类型添加成员. 咱们都知道,常见的类型成员,比如字段.属性.方法.事件.表示代码成 ...

随机推荐

  1. POJ 2975 Nim 尼姆博弈

    题目大意:尼姆博弈,求先手必胜的情况数 题目思路:判断 ans=(a[1]^a[2]--^a[n]),求ans^a[i] < a[i]的个数. #include<iostream> ...

  2. UVALive 2678 大于s的最短子序列和

    input n s 10<=n<=100000,s<1e9 a1 a2 ... an  ai<=10000 output 大于s的最短子序列和的长度,没有输出0 #includ ...

  3. group_concat()函数总结

    group_concat(),手册上说明:该函数返回带有来自一个组的连接的非NULL值的字符串结果.比较抽象,难以理解. 通俗点理解,其实是这样的:group_concat()会计算哪些行属于同一组, ...

  4. oracle的row_number()和rownum

    row_number() 函数和rownum的介绍: 1.row_number() 方法的格式: row_number()over([partition by col1] order by col2) ...

  5. php sso 单点登录的实现 代码示例

    先说一下这样做的好处吧,先来三个屌丝域名: www.openpoor.com myspace.openpoor.com passport.openpoor.com 大家都知道,虽然他们都是一个域名但主 ...

  6. ALAssetsLibrary 照片相关 浅析

    ALAssetsLibrary 提供了访问iOS设备下”照片”应用下所有照片和视频的接口: 从 ALAssetsLibrary 中可读取所有的相册数据,即 ALAssetsGroup 对象列表: 从每 ...

  7. libMobileGestalt与UDID

    libMobileGestalt与UDID 没有评论 在iOS中,libMobileGestalt动态库, 用来取得各种系统变量,比如UDID, 磁盘使用量, 设备版本 在iOS7中,对于开发者来说, ...

  8. HDU 4828 Grids(卡特兰数+乘法逆元)

    首先我按着我的理解说一下它为什么是卡特兰数,首先卡特兰数有一个很典型的应用就是求1~N个自然数出栈情况的种类数.而这里正好就对应了这种情况.我们要满足题目中给的条件,数字应该是从小到大放置的,1肯定在 ...

  9. Mysql笔记5之查询

    1查询所有的列 select *from student 2查询指定列 select name,age from student 3查询时候使用别名 select name as 别名,age as ...

  10. MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库【转载】

    转自 MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库 - _安静 - 博客园http://www.cnblogs.com/xionghui/archive/2012/ ...