前面我们已经完成了LwIP协议栈基于逻辑的基本移植,在这一节我们将以RAW API来实现UDP服务器。

1UDP协议简述

  UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,处于传输层,是IP协议的上层协议。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

  UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。

  UDP报头由4个域组成,其中每个域各占用2个字节,具体如下:源端口号、目标端口号、数据报长度、校验值。其数据结构如下:

  UDP协议使用端口号为不同的应用保留其各自的数据传输通道。UDP和TCP协议正是采用这一机制实现对同一时刻内多项应用同时发送和接收数据的支持。数据发送一方(可以是客户端或服务器端)将UDP数据包通过源端口发送出去,而数据接收一方则通过目标端口接收数据。有的网络应用只能使用预先为其预留或注册的静态端口;而另外一些网络应用则可以使用未被注册的动态端口。因为UDP报头使用两个字节存放端口号,所以端口号的有效范围是从0到65535。一般来说,大于49151的端口号都代表动态端口。

  数据报的长度是指包括报头和数据部分在内的总字节数。因为报头的长度是固定的,所以该域主要被用来计算可变长度的数据部分。数据报的最大长度根据操作环境的不同而各异。从理论上说,包含报头在内的数据报的最大长度为65535字节。不过,一些实际应用往往会限制数据报的大小,有时会降低到8192字节。

  UDP协议使用报头中的校验值来保证数据的安全。校验值首先在数据发送方通过特殊的算法计算得出,在传递到接收方之后,还需要再重新计算。如果某个数据报在传输过程中被第三方篡改或者由于线路噪音等原因受到损坏,发送和接收方的校验计算值将不会相符,由此UDP协议可以检测是否出错。

2UDP服务器设计

  前面我们简要的介绍了UDP协议及其数据报,接下来我们将考虑怎么实现基于UDP协议的服务器。

  首先,我们来看一看与UDP相关的API函数,并对它们作一个初步的介绍,应为我们需要使用它们来实现我们的应用。函数及说明如下:

  了解了这些函数,我们现在考虑其实现过程。对于UDP服务器端来说,实现相对简洁。其实现步骤如下:

  首先,生成一个新的UDP控制块。

  接着,绑定UDP控制块到任意IP地址及制定端口。

  最后,为UDP控制块注册数据处理回调函数,这里需要说明一下,这就是RAW AIP的回调函数。根据你要实现的功能不同复杂程度完全不一样。我们由于要实现一个回环服务器,所以相对简单。只需要将收到的信息,以我们想要的方式发送回客户端就可以了。

  为了很好的实现UDP服务器,还有一个问题需要设计好,就是我们前面我们曾提到的端口。我们都知道TCP/IP协议族包括有很多的协议,那通讯究竟是针对哪一个协议发生的呢?所谓两台机器间的通讯,实际上是主机上的应用进程间的通讯,端口号就是为了最终实现主机上应用进程的通讯。我们常见且会在后续使用到的协议端口如下:

  为了使用方便我们将这些端口定义为宏,并存储到一个专门的文件中。在这里我们本次实现UDP服务器也需要制定一个端口,其实支持UDP的端口都没问题,但为了方便描述我们制定其为回环显示端口。

3UDP服务器实现

  我们了解了其实现的基本过程,其实并不复杂。事实上,回调函数的内容才是我们真正需要考虑的东西。我们将其实现分为两个部分:一是UDP服务器的初始化部分;二是UDP服务器功能部分,也就是回调函数所执行的内容。

  首先实现UDP服务器的初始化部分。初始化部分定义一个新的UDP控制块,并将其绑定到任意IP地址及指定端口。然后注册数据处理回调函数。

 /* UDP初始化配置 */
void UDP_Server_Initialization(void)
{
static char * recv_arg="We recieved a UDP data\n";
struct udp_pcb *upcb; /* 生成一个新的UDP控制块 */
upcb = udp_new(); /* 绑定upcb块到任意IP地址及指定端口*/
udp_bind(upcb, IP_ADDR_ANY, UDP_ECHO_SERVER_PORT); /* 为upcb指定数据处理回调函数 */
udp_recv(upcb,UDPServerCallback,(void *)recv_arg);
}

  关于为什么要将本地IP绑定到任意IP呢?这是因为UDP服务器收到数据包后,LwIP会先判断其数据包的目的IP和端口是否和本地注册的PCB控制块绑定的本地的IP和本地端口号是否匹配。所以我们绑定PCB控制块本地IP设为IP_ADDR_ANY时,只要收到的数据包的目的IP非广播地址,端口号匹配,那么均认为数据包的目的IP和端口是与本地注册的PCB控制块绑定的本地IP和端口号相匹配的。省去了自己构造本地IP的过程。

  初始化完毕后,注册了数据处理回调函数。接下来需要实现回调函数的内容。回调函数主要实现对数据的处理,这取决于自己的需求。在这里我们在接收到UDP客户端数据包后,不对其作什么处理,因为这一数据本来无意义,我们对任何的客户端请求给予固定的回复。

 /* 定义UDP服务器数据处理回调函数 */
static void UDPServerCallback(void *arg,struct udp_pcb *upcb,struct pbuf *revBuf,const ip_addr_t *addr,u16_t port)
{
struct pbuf *sendBuf = NULL;
const char* reply = "This is reply!\n"; pbuf_free(revBuf); sendBuf = pbuf_alloc(PBUF_TRANSPORT, strlen(reply)+, PBUF_RAM);
if(!sendBuf)
{
return;
} memset(sendBuf->payload,,sendBuf->len);
memcpy(sendBuf->payload, reply, strlen(reply));
udp_sendto(upcb, sendBuf, addr, port);
pbuf_free(sendBuf);
}

  对于这个回调函数,它实际是赋给一个函数指针,所以虽然它的内容和名称可以随意,但其格式是有要求的:void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port)

4、结论

  至此,我们完成了简单的UDP服务器,在这里我们使用客户端来测试一下这个UDP服务器,测试结果如下:

  这里只测试了一个客户端两届服务器的情况,其实连接多个客户端的情况也是没问题的。如下:

  佷显然,如果我们希望实现更复杂的UDP服务器,我们只需要将我们想实现的功能做到回调函数中就可以了。

欢迎关注:

LwIP应用开发笔记之二:LwIP无操作系统UDP服务器的更多相关文章

  1. LwIP应用开发笔记之一:LwIP无操作系统基本移植

    现在,TCP/IP协议的应用无处不在.随着物联网的火爆,嵌入式领域使用TCP/IP协议进行通讯也越来越广泛.在我们的相关产品中,也都有应用,所以我们结合应用实际对相关应用作相应的总结. 1.技术准备 ...

  2. LwIP应用开发笔记之四:LwIP无操作系统TFTP服务器

    前面我们已经实现了UDP的回环客户端和回环服务器的简单应用,接下来我们实现一个基于UDP的简单文件传输协议TFTP. 1.TFTP协议简介 TFTP是TCP/IP协议族中的一个用来在客户机与服务器之间 ...

  3. LwIP应用开发笔记之五:LwIP无操作系统TCP服务器

    前面我们实现了UDP服务器及客户端以及基于其上的TFTP应用服务器.接下来我们将实现同样广泛应用的TCP协议各类应用. 1.TCP简述 TCP(Transmission Control Protoco ...

  4. LwIP应用开发笔记之三:LwIP无操作系统UDP客户端

    前一节我们实现了基于RAW API的UDP服务器,在接下来,我们进一步利用RAW API实现UDP客户端. 1.UDP协议简述 UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包 ...

  5. LwIP应用开发笔记之八:LwIP无操作系统HTTP客户端

    前面我们实现了TCP服务器和客户端的简单应用,接下来我们实现一个基于TCP协议的应用协议,那就是HTTP超文本传输协议 1.HTTP协议简介 超文本传输协议(Hyper Text Transfer P ...

  6. LwIP应用开发笔记之七:LwIP无操作系统HTTP服务器

    前面我们实现了TCP服务器和客户端的简单应用,接下来我们实现一个基于TCP协议的应用协议,那就是HTTP超文本传输协议 1.  HTTP协议简介   超文本传输协议(Hyper Text Transf ...

  7. LwIP应用开发笔记之六:LwIP无操作系统TCP客户端

    上一篇我们基于LwIP协议栈的RAW API实现了一个TCP服务器的简单应用,接下来一节我们来实现一个TCP客户端的简单应用. 1.TCP简述 TCP(Transmission Control Pro ...

  8. ArcGIS API for javascript开发笔记(二)——解决ArcGIS Service中的服务在内网环境下无法进行javascript预览问题

    感谢一路走来默默支持和陪伴的你~~~ ----------------------------拒绝转载-------------------------------- 1.问题说明 在使用ArcGIS ...

  9. stm32开发笔记(二):stm32系列使用V3.5固件库的帮助文件以及GPIO基本功能(一)

    前言   stm32系列是最常用的单片机之一,不同的版本对应除了引脚.外设.频率.容量等'不同之外,其开发的方法是一样的.  本章讲解使用库函数使用GPIO引脚功能.   补充   本文章为多年前学习 ...

随机推荐

  1. 洛谷P5092 [USACO2004OPEN]Cube Stacking 方块游戏 (带权并查集)

    题目描述 约翰和贝茜在玩一个方块游戏.编号为 1\ldots n 1-n 的 n n ( 1 \leq n \leq 30000 1≤n≤30000 )个方块正放在地上,每个构成一个立方柱. 游戏开始 ...

  2. python 使用 requests 做 http 请求

    1. get import requests # 最简单的get请求 r = requests.get(url) print(r.status_code) print(r.json()) # url ...

  3. [译] 在 UNIX 中,一切皆文件

    原文地址:In UNIX Everything is a File 原文作者:ph7spot.com 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:pmw ...

  4. cpu绘图的本质:生成图片的二进制(数字)信息

    计算机中图片是用二进制数据表达的.

  5. 强制结束虚拟机 centos home 卷丢失导致无法挂载进入 emergency mode 紧急模式

    参考文章: https://blog.51cto.com/13059784/2054378 xfs文件修复参考1:https://codeday.me/bug/20181112/367781.html ...

  6. js 实现页面点击按钮复制内容

    前言: 我们平时在页面中是按照长按来实现复制相关的内容,那么怎么用js实现点击按钮实现复制相关的内容呢?请看如下方法: 实现步骤: 1.引入相关的js(ClipboardJS插件) <scrip ...

  7. clr调试扩展和DAC

    SOS.DLL.SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展.简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用 ...

  8. Netflix-mantis 实时数据流开发平台

    mantis 是netflix 开源的已经在netflix 使用了多年的实时流处理平台,目前从官方文档的介绍,在netflix使用场景很多 使用场景 上下文报警 监控netflix 的微服务 异常追踪 ...

  9. ESA2GJK1DH1K微信小程序篇: 测试微信小程序扫描Air202上面的二维码绑定设备,并通过MQTT控制设备

    前言 一,微信小程序篇小程序下载(该功能为小程序篇基础功能源码) 实现功能概要 微信小程序通过扫描GPRS上的二维码,绑定GPRS设备.然后使用小程序通过GPRS远程控制开发板上的继电器, 远程显示单 ...

  10. 从Hello World 来讲解线程

    从一个经典的例子开始:一个打印“Hello World.”的程序.一个非常简单的在单线程中运行的Hello World程序如下所示,当我们谈到多线程时,它可以作为一个基准. #include<i ...