LwIP应用开发笔记之二:LwIP无操做系统UDP服务器

   前面咱们已经完成了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协议能够检测是否出错。spa

2UDP服务器设计设计

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

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

 

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

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

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

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

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

 

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

3UDP服务器实现

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

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

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

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

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

 1 /* 定义UDP服务器数据处理回调函数 */
 2 static void UDPServerCallback(void *arg,struct udp_pcb *upcb,struct pbuf *revBuf,const ip_addr_t *addr,u16_t port)
 3 {
 4   struct pbuf *sendBuf = NULL;
 5   const char* reply = "This is reply!\n";
 6  
 7   pbuf_free(revBuf);
 8   
 9   sendBuf = pbuf_alloc(PBUF_TRANSPORT, strlen(reply)+1, PBUF_RAM);
10   if(!sendBuf)
11   {
12     return;
13   }
14  
15   memset(sendBuf->payload,0,sendBuf->len);
16   memcpy(sendBuf->payload, reply, strlen(reply));
17   udp_sendto(upcb, sendBuf, addr, port);
18   pbuf_free(sendBuf);
19 }

  对于这个回调函数,它实际是赋给一个函数指针,因此虽然它的内容和名称能够随意,但其格式是有要求的: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服务器,咱们只须要将咱们想实现的功能作到回调函数中就能够了。

欢迎关注:

 

相关文章
相关标签/搜索