admin 管理员组

文章数量: 887021

RAW

UDP

  • UDP 是 User Datagram Protocol 的简称,中文名是用户数据报协议,是一种无连接、不可靠的协议,它只是简单地实现从一端主机到另一端主机的数据传输功能,这些数据通过 IP 层发送,在网络中传输,到达目标主机的顺序是无法预知的。
  • 发送的一端,UDP 只是把上层应用的数据封装到 UDP 报文中(直接就添加了个UDP首部),在差错检测方面,仅仅是对数据进行了简单的校验,然后将其封装到 IP 数据报中发送出去。(也就是封装好了,直接发送出去,然后什么都不管了
  • 接收端,无论是否收到数据,它都不会产生一个应答发送给源主机,并且如果接收到数据发送校验错误,那么接收端就会丢弃该 UDP 报文,也不会告诉源主机,这样子传输的数据是无法保障其准确性的,如果想要其准确性,那么就需要应用程序来保障了。(直接拆包,检验错误就直接丢弃
  • 与 TCP 协议一样,UDP 报文协议根据对应的端口号传递到目标主机的应用线程
  • 支持一对一,一对多,多对一,多对多的交互通信。(而TCP只能一对一)

UDP 控制块

  • 与 TCP 协议一样,为了更好管理 UDP 报文,LwIP 定义了一个 UDP 控制块
  • 记录与 UDP 通信的所有信息,如源端口号、目标端口号、源 IP 地址、目标 IP 地址以及收到数据时候的回调函数等等
  • 系统会为每一个基于 UDP 协议的应用线程创建一个 UDP 控制块,并且将其与对应的端口绑定,这样子就能进行 UDP 通信了
  • 与 TCP 协议一样,LwIP 会把多个这样子的 UDP 控制块用一个链表连接起来,在处理的时候遍历列表,然后对控制块进行操作
struct udp_pcb 
{IP_PCB;			//宏IP_PCB中的各个字段struct udp_pcb 		*next;	//指向udp_pcb,用于将控制块组织成链表u8_t 			flags;	//控制块状态字段u16_t local_port, remote_port;  	//本地端口号和目的端口号udp_recv_fn 		recv;     	//处理数据的回调函数void 			*recv_arg;  //传递给回调函数(就上面这个)的参数
};

IP_PCB:

  • IP层的宏定义,应该就是给IP层用的
  • 里面包括 IP 层需要使用的信息,如本地 IP 地址与目标 IP 地址(或者称为远端 IP 地址),服务类型、网卡、生存时间等
/* This is the common part of all PCB types. It needs to be at thebeginning of a PCB type definition. It is located here so thatchanges to this common part are made in one location instead ofhaving to change all PCB structs. */
#define IP_PCB \/* ip addresses in network byte order */ \ip_addr_t local_ip; \      //本机ipip_addr_t remote_ip; \     //目的端口号/* Socket options */  \u8_t so_options;      \/* Type Of Service */ \u8_t tos;              \/* Time To Live */     \u8_t ttl               \/* link layer address resolution hint */ \IP_PCB_ADDRHINT

flags:

#define UDP_FLAGS_NOCHKSUM       0x01U  //不进行帧校验和
#define UDP_FLAGS_UDPLITE        0x02U
#define UDP_FLAGS_CONNECTED      0x04U  //已经建立连接了(就是访问了,UDP是不建立连接的,数据传输了就好了)
#define UDP_FLAGS_MULTICAST_LOOP 0x08U

端口号:

  • 本地端口号与目标(远端)端口号
  • UDP 协议就是根据这些端口号识别应用线程,当 UDP 收到一个报文的时候,会遍历链表上的所有控制块,根据报文的目标端口号找到与本地端口号相匹配的 UDP 控制块,然后递交数据到上层应用,而如果找不到对应的端口号,那么就会返回一个端口不可达 ICMP 差错控制报文。

recv:是一个函数指针(也就说回调函数),当UDP接收到数据后,就通过这个函数对数据进行处理

  • 使用 NETCONN API 或者是 Socket API 编程,是不需要我们自己去注册回调函数recv_udp(),因为这个函数 LwIP 内核会自动给我们注册
/** Function prototype for udp pcb receive callback functions* addr and port are in same byte order as in the pcb* The callback is responsible for freeing the pbuf* if it's not used any more.** ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf*            makes 'addr' invalid, too.** @param arg user supplied argument (udp_pcb.recv_arg)   就是控制块中写到的参数* @param pcb the udp_pcb which received data   * @param p the packet buffer that was received  接收到的数据* @param addr the remote IP address from which the packet was received 发送数据端的IP地址* @param port the remote port from which the packet was received  发送数据端的端口号*/typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,ip_addr_t *addr, u16_t port);

API

err

	err_t err;
/** Definitions for error constants. */
typedef enum {
/** No error, everything OK. */ERR_OK         = 0,
/** Out of memory error.     */ERR_MEM        = -1,
/** Buffer error.            */ERR_BUF        = -2,
/** Timeout.                 */ERR_TIMEOUT    = -3,
/** Routing problem.         */ERR_RTE        = -4,
/** Operation in progress    */ERR_INPROGRESS = -5,
/** Illegal value.           */ERR_VAL        = -6,
/** Operation would block.   */ERR_WOULDBLOCK = -7,
/** Address in use.          */ERR_USE        = -8,
/** Already connecting.      */ERR_ALREADY    = -9,
/** Conn already established.*/ERR_ISCONN     = -10,
/** Not connected.           */ERR_CONN       = -11,
/** Low-level netif error    */ERR_IF         = -12,/** Connection aborted.      */ERR_ABRT       = -13,
/** Connection reset.        */ERR_RST        = -14,
/** Connection closed.       */ERR_CLSD       = -15,
/** Illegal argument.        */ERR_ARG        = -16
} err_enum_t;

新建控制块 udp_new()

在使用 UDP 协议进行通信之前,必须创建一个 UDP 控制块,然后将控制块与对应的端口号进行绑定,才能发送报文,而在接收 UDP 报文的时候,这个端口号就是 UDP 报文唯一识别的标志,否则 UDP 报文将无法递交到应用层去处理,即无法通过 UDP 控制块的接收回调函数递交给应用层,新建控制块的函数很简单,就是在内存池中申请一个 MEMP_UDP_PCB 类型的内存块,用于存放 UDP 控制块的相关信息,并将其初始化为 0

//使用
udppcb=udp_new();//新建一个UDP控制块
/*** @ingroup udp_raw* Creates a new UDP pcb which can be used for UDP communication. The* pcb is not active until it has either been bound to a local address* or connected to a remote address.** @return The UDP PCB which was created. NULL if the PCB data structure* could not be allocated.** @see udp_remove()*/
struct udp_pcb *
udp_new(void)
{struct udp_pcb *pcb;LWIP_ASSERT_CORE_LOCKED();pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);/* could allocate UDP PCB? */if (pcb != NULL) {/* UDP Lite: by initializing to all zeroes, chksum_len is set to 0* which means checksum is generated over the whole datagram per default* (recommended as default by RFC 3828). *//* initialize PCB to all zeroes */memset(pcb, 0, sizeof(struct udp_pcb));pcb->ttl = UDP_TTL;
#if LWIP_MULTICAST_TX_OPTIONSudp_set_multicast_ttl(pcb, UDP_TTL);
#endif /* LWIP_MULTICAST_TX_OPTIONS */}return pcb;
}

建立会话 udp_connect()

  • 说明:本来是想写建立连接的,但是对于 UDP 协议来说,建立连接的这种说法并不太准确,因为 UDP 协议本身就是一个无连接协议,因此,我们就说建立 UDP 会话好了。
  • 因为UDP是直接发送不需要建立连接之后再发送数据。
  • 这个函数的作用:1、设置控制块中的远端 IP 地址与端口号。2、然后将 UDP 控制块的状态设置为会话状态 UDP_FLAGS_CONNECTED。3、并且将 UDP 控制块插入 udp_pcbs 链表中
//举例 第一个参数控制块 目的ip(要给地址)  目的端口号
err=udp_connect(udppcb,&rmtipaddr,UDP_DEMO_PORT);//UDP客户端连接到指定IP地址和端口号的服务器
/*** Connect an UDP PCB.** This will associate the UDP PCB with the remote address.** @param pcb UDP PCB to be connected with remote address ipaddr and port.* @param ipaddr remote IP address to connect with.* @param port remote UDP port to connect with.** @return lwIP error code** ipaddr & port are expected to be in the same byte order as in the pcb.** The udp pcb is bound to a random local port if not already bound.** @see udp_disconnect()*/
err_t
udp_connect(struct udp_pcb 

本文标签: raw