UDP通信协议详解
| 中文名 | ????????????????用戶數(shù)據(jù)報(bào)協(xié)議 |
| 英文名 | User Datagram Protocol |
| 基礎(chǔ) | IP數(shù)據(jù)包服務(wù)上增加一點(diǎn)功能 |
| 類別 | 傳輸層協(xié)議 |
| 特點(diǎn) | 無(wú)連接、不可靠、快速傳輸 |
| 用途 | 發(fā)送IP數(shù)據(jù)包 |
如右圖所所示為udp協(xié)議的基本信息
上圖就是UDP的數(shù)據(jù)報(bào)服務(wù),sendto兩次發(fā)送的是單獨(dú)的兩個(gè)個(gè)體,接收端也就必須recvfrom兩次,所以UDP沒(méi)有粘包這種概念!
UDP(User Datagram Protocol)用戶數(shù)據(jù)報(bào)協(xié)議,非連接的協(xié)議,傳輸數(shù)據(jù)之前源端和終端不 建立連接,當(dāng)它想傳送時(shí)就簡(jiǎn)單地去抓取來(lái)自應(yīng)用程序的數(shù)據(jù),并盡可能快地把它扔到網(wǎng)絡(luò)上。 在發(fā)送端,UDP傳送數(shù)據(jù)的速度僅僅是受應(yīng)用程序生成數(shù)據(jù)的速度、計(jì)算機(jī)的能力和傳輸帶寬 的限制;在接收端,UDP把每個(gè)消息段放在隊(duì)列中,應(yīng)用程序每次從隊(duì)列中讀一個(gè)消息段。 相比TCP就是無(wú)需建立鏈接,結(jié)構(gòu)簡(jiǎn)單,無(wú)法保證正確性,容易丟包(菜鳥(niǎo)教程節(jié)選)。
UDP的不可靠性:
上面我們?cè)诒砀裰幸舱f(shuō)到了UDP協(xié)議它的特點(diǎn)就是無(wú)連接、不可靠、快速傳輸,先來(lái)說(shuō)下它為什么不可靠,或者說(shuō)它的不可靠可以為我們帶來(lái)什么別的方面的優(yōu)化:
? ? ? ? 我們都知道TCP是流傳輸且它的特點(diǎn)是可靠(因?yàn)樗枰_認(rèn)應(yīng)答、超時(shí)重傳這些操作),而在UDP傳輸則不需要哪些操作(TCP就像我們打電話,得知道對(duì)方的電話號(hào)碼,還得等對(duì)方接聽(tīng)起來(lái)才可以就那些通話,但是UDP的話就像是發(fā)短信,只需要知道號(hào)碼就可以執(zhí)行了,至于發(fā)送成功了沒(méi)這不重要,只要發(fā)出去就好了)
UDP的應(yīng)用場(chǎng)景:
? ? ? ? 在我們視頻通話的時(shí)候用的就是UDP傳輸,假設(shè)A,B兩人再進(jìn)行視頻通話,其中A網(wǎng)絡(luò)有點(diǎn)慢導(dǎo)致接收B的數(shù)據(jù)慢的很,但是B還是按照他那邊網(wǎng)速來(lái)勁傳輸,這樣就會(huì)導(dǎo)致兩人在視頻通話過(guò)程中丟包,但是因?yàn)檫@是UDP協(xié)議傳輸,所以無(wú)所謂啊,丟包就丟包,等A的網(wǎng)速好起來(lái)了再問(wèn)下說(shuō)你剛剛說(shuō)了啥,B重新說(shuō)一遍就好.
? ? ? ? 假設(shè)視頻通話使用的是TCP協(xié)議傳輸?shù)脑?在A網(wǎng)絡(luò)卡頓的時(shí)候就會(huì)造成接收緩沖區(qū)堆積,等A網(wǎng)絡(luò)好的時(shí)候看到的B相當(dāng)于是幾分鐘之前的B,相當(dāng)于看的B的錄屏,這在視頻通話中是根本不行的,所以TCP和UDP協(xié)議適用于不同的場(chǎng)景之下:TCP適合那種不允許丟包的網(wǎng)絡(luò)傳輸中,而UDP協(xié)議適用于那種允許丟包的網(wǎng)絡(luò)傳輸中(UDP效率很高)
大家不熟悉TCP的可以看下這篇:
TCP協(xié)議的服務(wù)器與客戶端的程序設(shè)計(jì)(代碼注釋超詳細(xì))_神廚小福貴!的博客-CSDN博客
UDP客戶端和服務(wù)器端編程示例
服務(wù)器端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);//創(chuàng)建套接字 SOCK_DGRAM這個(gè)為UDP數(shù)據(jù)報(bào)套接字assert(sockfd != -1);struct sockaddr_in saddr,caddr; //sockaddr_in在頭文件#include<netinet/in.h>或#include <arpa/inet.h>中定義,saddr代表服務(wù)器端地址 caddr代表客戶端地址memset(&saddr,0,sizeof(saddr));//saddr其實(shí)有四項(xiàng)成員,最后一項(xiàng)用來(lái)占位的,必須搞為0,索性我們開(kāi)始直接給全部置為0,后面再來(lái)綁定ip和端口saddr.sin_family = AF_INET;//地址族,TCP/ipv4協(xié)議族saddr.sin_port = htons(6000);//端口為小端序列,htons轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,也是大端字節(jié)序(一般使用都是5000以上,5000以內(nèi)一般都是特定使用的,比如你辦了個(gè)手機(jī)卡,你能用110這個(gè)號(hào)碼嘛,博客園因?yàn)?10有特殊意義,一個(gè)道理)sadd r.sin_addr.s_addr = inet_addr("192.168.0.108");//自己本地的IP地址(終端ifconfig查詢自己的IP)//inet_addr將點(diǎn)分十進(jìn)制轉(zhuǎn)換為午飯后整型int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//將sockfd和本地IP綁定//為什么要這個(gè)呢(struct sockaddr*)強(qiáng)轉(zhuǎn)呢,bind這個(gè)參數(shù)類型為struct sockaddr與sockaddr_in類型不一致,所以強(qiáng)轉(zhuǎn)assert(res != -1);// UDP協(xié)議傳輸中沒(méi)有監(jiān)聽(tīng)隊(duì)列這個(gè)東西while(1){int len = sizeof(caddr);char buff[128] = {0};recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len); //接收客戶端發(fā)的數(shù)據(jù)printf("buff = %s",buff);sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr)); //給客戶端發(fā)送okclose(c);}
}
客戶端:(和服務(wù)器端比起來(lái)就是,沒(méi)有那個(gè)bind\recvfrom和sendto的順序)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd = socket(AF_INET,SOCK_DGRAM,0);//創(chuàng)建套接字assert(sockfd != -1);struct sockaddr_in saddr,caddr; //sockaddr_in在頭文件#include<netinet/in.h>或#include <arpa/inet.h>中定義,saddr代表服務(wù)器端地址 caddr代表客戶端地址memset(&saddr,0,sizeof(saddr));//saddr其實(shí)有四項(xiàng)成員,最后一項(xiàng)用來(lái)占位的,必須搞為0,索性我們開(kāi)始直接給全部置為0,后面再來(lái)綁定ip和端口saddr.sin_family = AF_INET;//地址族,TCP/ipv4協(xié)議族saddr.sin_port = htons(6000);//端口為小端序列,htons轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序,也是大端字節(jié)序(一般使用都是5000以上,5000以內(nèi)一般都是特定使用的,比如你辦了個(gè)手機(jī)卡,你能用110這個(gè)號(hào)碼嘛,博客園因?yàn)?10有特殊意義,一個(gè)道理)sadd r.sin_addr.s_addr = inet_addr("192.168.0.108");//自己本地的IP地址(終端ifconfig查詢自己的IP)//inet_addr將點(diǎn)分十進(jìn)制轉(zhuǎn)換為午飯后整型while(1){char budd[128] = {0};fgets(buff,128,stdin);if(strncmp(buff,"end",3) == 0){break;}sendto(sockfd,buff,strlen(buff)-1,0,(struct sockaddr*)&saddr,sizeof(saddr));//TCP中參數(shù)只需要寫(xiě)到0那塊 主要是TCP中可以通過(guò)描述符查詢到底層端口memset(buff,0,128);int len = sizeof(saddr);recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);printf("buff = %s \n",buff);}close(sockfd);
}
不可靠的解決?
我們都知道UDP傳輸不可靠,那么如何解決呢?有種笨辦法就是在每次傳輸完數(shù)據(jù)之后必須接收recvfrom一個(gè)數(shù)據(jù)才可以得以繼續(xù)進(jìn)行,否則就堵塞在revcfrom那塊,那么這種方法不就和TCP一樣了嗎(并且可能自己在應(yīng)用層實(shí)現(xiàn)的應(yīng)答確認(rèn)機(jī)制沒(méi)有TCP中的應(yīng)答確認(rèn)機(jī)制效率高而將它弄成四不像),且丟失了UDP的最大特點(diǎn)-------效率.(最后這段話完全是自己的想法,不知道正確與否,如有不恰當(dāng)之處,歡迎各位指出)
"好好學(xué)習(xí),趁著還來(lái)得及!!!"
總結(jié)
- 上一篇: 金钱龟多少钱啊?
- 下一篇: “心闲无所思”下一句是什么