LWIP之UDP协议
IP協(xié)議提供了在各個(gè)主機(jī)之間傳送數(shù)據(jù)報(bào)的功能,但是數(shù)據(jù)的最終目的地是主機(jī)上的特定應(yīng)用程序。傳輸層協(xié)議就承擔(dān)了這樣的責(zé)任,典型的傳輸層協(xié)議有UDP和TCP兩種。許多著名的上層應(yīng)用都是基于UDP實(shí)現(xiàn)的,比如DNS、DHCP、IGMP、SNMP等。
?
UDP協(xié)議稱為用戶數(shù)據(jù)包協(xié)議,是一種無(wú)連接、不可靠的傳輸協(xié)議。UDP協(xié)議只是簡(jiǎn)單地完成應(yīng)用程序到應(yīng)用程序的交互,并不提供流量控制機(jī)制、復(fù)雜的差錯(cuò)控制方法、確認(rèn)機(jī)制。雖然UDP可靠性非常差,但是UDP適用于那些輕微數(shù)據(jù)差錯(cuò)不敏感的應(yīng)用,比如視頻傳輸、網(wǎng)絡(luò)電話等。
?
每臺(tái)主機(jī)都包含稱為協(xié)議端口的抽象目的點(diǎn),端口號(hào)范圍為0~65535,進(jìn)程綁定到端口號(hào)上。這樣數(shù)據(jù)包只要遞交到相應(yīng)的端口即可。
?
報(bào)文格式
偽首部:真實(shí)傳輸?shù)腢DP數(shù)據(jù)包中沒有該字段,只在計(jì)算校驗(yàn)和時(shí)虛擬出該字段。
struct udp_hdr {PACK_STRUCT_FIELD(u16_t src); //源端口號(hào)PACK_STRUCT_FIELD(u16_t dest); //目的端口號(hào)PACK_STRUCT_FIELD(u16_t len); //總長(zhǎng)度(UDP首部+UDP數(shù)據(jù))PACK_STRUCT_FIELD(u16_t chksum); //校驗(yàn)和(UDP偽首部+UDP首部+UDP數(shù)據(jù))(0:不計(jì)算校驗(yàn)和 0xFFFF:檢驗(yàn)和為0) } PACK_STRUCT_STRUCT;?
先看一下UDP控制塊
/* 所有控制塊共有部分 */ #define IP_PCB \struct ip_addr local_ip; \ //本機(jī)IPstruct ip_addr remote_ip; \ //遠(yuǎn)端IPu16_t so_options; \ //套接字選項(xiàng)u8_t tos; \ //服務(wù)類型u8_t ttl \ //生存時(shí)間IP_PCB_ADDRHINT/* 不進(jìn)行校驗(yàn) */ #define UDP_FLAGS_NOCHKSUM 0x01U /* UDP-LITE協(xié)議 */ #define UDP_FLAGS_UDPLITE 0x02U /* 已綁定遠(yuǎn)程端口 */ #define UDP_FLAGS_CONNECTED 0x04U/* UDP控制塊 */ struct udp_pcb {IP_PCB; //IP控制塊相關(guān)字段struct udp_pcb *next; //用于將UDP控制塊連接成鏈表u8_t flags; //控制塊狀態(tài)u16_t local_port, remote_port; //本地端口、遠(yuǎn)程端口/* 接收回調(diào)函數(shù) */void (* recv)(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port);/* 用戶提供的接收回調(diào)函數(shù)參數(shù) */void *recv_arg; }; /* UDP控制塊鏈表 */ extern struct udp_pcb *udp_pcbs;UDP控制塊最終被組織成一張表
?
下面分析UDP創(chuàng)建和刪除的一些API
/* 創(chuàng)建一個(gè)UDP控制塊 */ struct udp_pcb *udp_new(void) {struct udp_pcb *pcb;/* 為UDP控制塊申請(qǐng)內(nèi)存空間 */pcb = memp_malloc(MEMP_UDP_PCB);if (pcb != NULL) {/* 清空UDP控制塊 */memset(pcb, 0, sizeof(struct udp_pcb));/* 設(shè)置UDP控制塊的TTL值 */pcb->ttl = UDP_TTL;}return pcb; } /* 綁定本地端口,0表示自動(dòng)分配端口號(hào) */ err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) {struct udp_pcb *ipcb;u8_t rebind;rebind = 0;/* 遍歷UDP控制塊鏈表 */for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {/* 該控制塊已經(jīng)綁定本地端口 */if (pcb == ipcb) {rebind = 1;}}/* 設(shè)置UDP控制塊的本地IP地址 */ip_addr_set(&pcb->local_ip, ipaddr);/* 自動(dòng)分配端口號(hào) */if (port == 0) { #define UDP_LOCAL_PORT_RANGE_START 4096 #define UDP_LOCAL_PORT_RANGE_END 0x7fff/* 遍歷整個(gè)UDP控制塊鏈表,查找最小的未使用端口號(hào) */port = UDP_LOCAL_PORT_RANGE_START;ipcb = udp_pcbs;while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) {if (ipcb->local_port == port) {port++;ipcb = udp_pcbs;} elseipcb = ipcb->next;}/* 沒有可用端口號(hào) */if (ipcb != NULL) {return ERR_USE;}}/* 設(shè)置本地端口號(hào) */pcb->local_port = port;/* 將UDP控制塊插入鏈表 */if (rebind == 0) {pcb->next = udp_pcbs;udp_pcbs = pcb;}return ERR_OK; } /* 綁定遠(yuǎn)程端口 */ err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) {struct udp_pcb *ipcb;/* 沒有綁定本地端口 */if (pcb->local_port == 0) {/* 先綁定本地端口 */err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);if (err != ERR_OK)return err;}/* 綁定遠(yuǎn)程端口 */ip_addr_set(&pcb->remote_ip, ipaddr);pcb->remote_port = port;/* 將UDP控制塊狀態(tài)設(shè)置為已綁定遠(yuǎn)程端口 */pcb->flags |= UDP_FLAGS_CONNECTED;/* UDP控制塊如果沒有插入鏈表,則將其插入鏈表 */for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {if (pcb == ipcb) {return ERR_OK;}}pcb->next = udp_pcbs;udp_pcbs = pcb;return ERR_OK; } /* 取消綁定遠(yuǎn)程端口 */ void udp_disconnect(struct udp_pcb *pcb) {/* 重置UDP控制塊遠(yuǎn)程IP和端口號(hào) */ip_addr_set(&pcb->remote_ip, IP_ADDR_ANY);pcb->remote_port = 0;/* 將UDP控制塊狀態(tài)設(shè)置為未綁定遠(yuǎn)程端口 */pcb->flags &= ~UDP_FLAGS_CONNECTED; } /* 設(shè)置UDP控制塊回調(diào)函數(shù),和回調(diào)函數(shù)用戶參數(shù) */ void udp_recv(struct udp_pcb *pcb, void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, u16_t port), void *recv_arg) {pcb->recv = recv;pcb->recv_arg = recv_arg; } /* 刪除UDP控制塊 */ void udp_remove(struct udp_pcb *pcb) {struct udp_pcb *pcb2;/* 將UDP控制塊從鏈表中移除 */if (udp_pcbs == pcb) {udp_pcbs = udp_pcbs->next;} elsefor (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {if (pcb2->next != NULL && pcb2->next == pcb) {pcb2->next = pcb->next;}}/* 釋放UDP控制塊內(nèi)存空間 */memp_free(MEMP_UDP_PCB, pcb); }接下來(lái)看一下UDP發(fā)送API
/* 發(fā)送UDP數(shù)據(jù)包(已經(jīng)綁定遠(yuǎn)程主機(jī)端口) */ err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) {return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); }/* 指定遠(yuǎn)程端口,發(fā)送UDP數(shù)據(jù)包 */ err_t udp_sendto(struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port) {struct netif *netif;/* 根據(jù)IP地址選擇一個(gè)合適(和目的主機(jī)處于同一子網(wǎng))的網(wǎng)絡(luò)接口 */netif = ip_route(dst_ip);if (netif == NULL) {return ERR_RTE;}/* 指定網(wǎng)絡(luò)接口發(fā)送UDP數(shù)據(jù)包 */return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); }/* 指定網(wǎng)絡(luò)接口和遠(yuǎn)程端口,發(fā)送UDP數(shù)據(jù)包 */ err_t udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif) {struct udp_hdr *udphdr;struct ip_addr *src_ip;err_t err;struct pbuf *q;/* 沒有綁定本地端口,先綁定本地端口 */if (pcb->local_port == 0) {err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);if (err != ERR_OK) {return err;}}/* 向前調(diào)整出UDP頭部空間,調(diào)整失敗 */if (pbuf_header(p, UDP_HLEN)) {/* 申請(qǐng)UDP頭部空間 */q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);if (q == NULL) {return ERR_MEM;}/* 將兩個(gè)pbuf串起來(lái) */pbuf_chain(q, p);}/* 調(diào)整成功 */else {/* 直接獲取pbuf指針 */q = p;}/* UDP頭部指針 */udphdr = q->payload;/* 設(shè)置UDP頭部源端口號(hào) */udphdr->src = htons(pcb->local_port);/* 設(shè)置UDP頭部目的端口號(hào) */udphdr->dest = htons(dst_port);/* 0表示不校驗(yàn) */udphdr->chksum = 0x0000; /* UDP本地IP為0,表示自動(dòng)選擇合適的網(wǎng)絡(luò)接口發(fā)送 */if (ip_addr_isany(&pcb->local_ip)) {/* 設(shè)置源IP地址 */src_ip = &(netif->ip_addr);} else {/* 本地IP地址和合適的網(wǎng)絡(luò)接口IP地址不相同 */if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) {/* 釋放數(shù)據(jù)包,返回錯(cuò)誤 */if (q != p) {pbuf_free(q);q = NULL;}return ERR_VAL;}/* 本地IP地址和合適的網(wǎng)絡(luò)接口IP地址相同 *//* 設(shè)置源IP地址 */src_ip = &(pcb->local_ip);}{/* 設(shè)置UDP頭部中的總長(zhǎng)度字段 */udphdr->len = htons(q->tot_len);/* 必須進(jìn)行和校驗(yàn) */if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {/* 計(jì)算并設(shè)置UDP和校驗(yàn) */udphdr->chksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len);if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff;}/* 將UDP數(shù)據(jù)包通過(guò)IP輸出函數(shù)發(fā)送出去 */err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif);}/* 釋放數(shù)據(jù)包空間 */if (q != p) {pbuf_free(q);q = NULL;}return err; }最后看一下接口函數(shù)
/* UDP數(shù)據(jù)包接收處理函數(shù) */ void udp_input(struct pbuf *p, struct netif *inp) {struct udp_hdr *udphdr;struct udp_pcb *pcb, *prev;struct udp_pcb *uncon_pcb;struct ip_hdr *iphdr;u16_t src, dest;u8_t local_match;u8_t broadcast;/* IP頭部 */iphdr = p->payload;/* 檢驗(yàn)UDP長(zhǎng)度是否合理,pbuf指針向后調(diào)整剝離IP頭部 */if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) {pbuf_free(p);goto end;}/* UDP頭部指針 */udphdr = (struct udp_hdr *)p->payload;/* 判斷IP地址是不是廣播地址 */broadcast = ip_addr_isbroadcast(&(iphdr->dest), inp);/* 取出源IP和目的IP */src = ntohs(udphdr->src);dest = ntohs(udphdr->dest);{prev = NULL;local_match = 0;uncon_pcb = NULL;/* 遍歷UDP控制塊鏈表 */for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {local_match = 0;/* 判斷該UDP數(shù)據(jù)包是不是發(fā)給該UDP控制塊(廣播、IP和端口號(hào)相同) */if ((pcb->local_port == dest) && ((!broadcast && ip_addr_isany(&pcb->local_ip)) || ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)) || (broadcast))) {/* 匹配成功 */local_match = 1;/* 記錄第一個(gè)匹配到的沒有綁定遠(yuǎn)程端口的控制塊 */if ((uncon_pcb == NULL) && ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {uncon_pcb = pcb;}}/* 判斷該數(shù)據(jù)包是不是綁定的遠(yuǎn)程端口相同 */if ((local_match != 0) && (pcb->remote_port == src) && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) {/* 將控制塊移到鏈表首部,增加效率 */if (prev != NULL) {prev->next = pcb->next;pcb->next = udp_pcbs;udp_pcbs = pcb;}break;}prev = pcb;}/* 沒有找到完全匹配的,就用沒有綁定遠(yuǎn)程端口的控制塊 */if (pcb == NULL) {pcb = uncon_pcb;}}/* 匹配到合適的控制塊 */if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) {{/* 需要檢查校驗(yàn)和 */if (udphdr->chksum != 0) {/* 進(jìn)行檢驗(yàn)和檢查(UDP偽首部+UDP首部+UDP數(shù)據(jù)) */if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDP, p->tot_len) != 0) {pbuf_free(p);goto end;}}}/* 向后調(diào)整剝離UDP首部 */if(pbuf_header(p, -UDP_HLEN)) {pbuf_free(p);goto end;}/* 已經(jīng)匹配到UDP控制塊 */if (pcb != NULL) {/* 調(diào)用UDP接收回調(diào)函數(shù) */if (pcb->recv != NULL) {pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src);} else {pbuf_free(p);goto end;}} /* 沒有匹配到UDP控制塊 */else {/* 該UDP數(shù)據(jù)包不是廣播也是不組播 */if (!broadcast && !ip_addr_ismulticast(&iphdr->dest)) {/* 發(fā)送ICMP目的不可達(dá)報(bào)文(端口不可達(dá)) */pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN);icmp_dest_unreach(p, ICMP_DUR_PORT);}pbuf_free(p);}} else {pbuf_free(p);} end:return; }?
UDP客戶端步驟
1.udp_new,創(chuàng)建UDP控制塊
2.udp_bind,綁定本地端口(該步驟可省略,省略時(shí)自動(dòng)分配本地端口號(hào))
3.udp_recv,注冊(cè)接收回調(diào)函數(shù)
4.udp_sendto,指定遠(yuǎn)程端口,發(fā)送數(shù)據(jù)包
或
1.udp_new,創(chuàng)建UDP控制塊
2.udp_bind,綁定本地端口(該步驟可省略,省略時(shí)自動(dòng)分配本地端口號(hào))
3.udp_connect,綁定遠(yuǎn)程端口
4.udp_recv,注冊(cè)接收回調(diào)函數(shù)
5.udp_send,發(fā)送數(shù)據(jù)包
?
UDP服務(wù)器步驟
1.udp_new,創(chuàng)建UDP控制塊
2.udp_bind,綁定本地端口
3.udp_recv,注冊(cè)接收回調(diào)函數(shù)
4.udp_sendto,指定遠(yuǎn)程端口,發(fā)送數(shù)據(jù)包
總結(jié)
以上是生活随笔為你收集整理的LWIP之UDP协议的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 再见,金庸;再见,江湖
- 下一篇: uboot命令体系