Linux之UDP協(xié)議及其編程全流程
UDP協(xié)議的特點(diǎn)
UDP 不提供可靠性的傳輸,它只是把應(yīng)用程序傳給 IP 層的數(shù)據(jù)報(bào)發(fā)送出去,但是并不能保證它們能到達(dá)目的地。
由于 UDP 在傳輸數(shù)據(jù)報(bào)前不用在客戶和服務(wù)器之間建立一個(gè)連接,且沒(méi)有超時(shí)重發(fā)等機(jī)制,故而傳輸速度很快。
- 無(wú)連接
- 不可靠
- 數(shù)據(jù)報(bào)服務(wù)
UDP發(fā)出的數(shù)據(jù)包不經(jīng)過(guò)確認(rèn),可以繼續(xù)發(fā)送。發(fā)送成功與否都不管,盡最大能力去發(fā)送,丟包也不負(fù)責(zé)。有自己的使用特點(diǎn):適合于做視頻(實(shí)時(shí)性)適合于即使丟包了,處理起來(lái)也比較方便。
適合于攝像頭以恒定速率發(fā),對(duì)方以恒定速率收,丟包了繼續(xù)發(fā),可以實(shí)時(shí)。
但是如果是TCP,如果丟包,會(huì)重發(fā),時(shí)間花銷大了,不能實(shí)時(shí)。不適合做攝像頭和視頻。
UDP的編程流程

UDP接口原型
接收
int recvfrom(int sockfd,void *buf,size_t size,int flag,struct sockaddr *peer_addr,socklen_t *addr_len);
peer_addr:用來(lái)保存recvfrom接收到的數(shù)據(jù)是來(lái)自哪臺(tái)主機(jī)的地址信息addr_len:地址結(jié)構(gòu)的長(zhǎng)度
發(fā)送
int sendto(int sockfd,void *buf,size_t size,int flag,struct sockaddr *peer_addr,socklen_t addr_len);
peer_addr:用來(lái)指定數(shù)據(jù)的接收方的地址信息addr_len:地址信息的長(zhǎng)度
示例代碼
UDP服務(wù)器端
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{
//SOCK_DGRAM表示使用的是UDP協(xié)議
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
//將主機(jī)字節(jié)序轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序
ser_addr.sin_port = htons(6000);
//將點(diǎn)分十進(jìn)制的地址字符串轉(zhuǎn)為unit32類型的值
ser_addr.sin_addr.s_addr = inet_addr("192.168.246.128");
int res = bind(sockfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
assert(res != -1);
//循環(huán)接受不同客戶端的數(shù)據(jù)
while(1)
{
char buff[128] = {0};
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int n = recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli_addr,&cli_len);
if(n <= 0)
{
break;
}
printf("%s:%d -- %s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff);
n = sendto(sockfd,"OK",2,0,(struct sockaddr*)&cli_addr,cli_len);
if(n <= 0)
{
break;
}
}
close(sockfd);
exit(0);
}
UDP客戶端
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
assert(sockfd != -1);
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(6000);
ser_addr.sin_addr.s_addr = inet_addr("192.168.246.128");
while(1)
{
printf("請(qǐng)輸入:");
char buff[128] = {0};
fgets(buff,127,stdin);
if(strncmp(buff,"end",3) == 0)
{
break;
}
int n = sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
if(n <= 0)
{
break;
}
memset(buff,0,128);
int m = recvfrom(sockfd,buff,127,0,NULL,NULL);//服務(wù)器地址信息已知,無(wú)需保存直接傳入NULL
if(m <= 0)
{
break;
}
printf("%s\n",buff);
}
close(sockfd);
exit(0);
}
兩個(gè)客戶端同時(shí)向服務(wù)器端發(fā)送信息

多個(gè)客戶端可以和服務(wù)器一起鏈接通訊。recvfrom并不是只等第一個(gè)或者第二個(gè)客戶端,而是誰(shuí)給它發(fā),它就收誰(shuí)的。
如果在客戶端保持運(yùn)行狀態(tài)的情況下,將服務(wù)器端關(guān)閉,然后再把服務(wù)器端重新運(yùn)行起來(lái),這時(shí)候客戶端發(fā)送數(shù)據(jù),服務(wù)器端是可以收到的。
因?yàn)閁DP本來(lái)就沒(méi)有建立連接。如果服務(wù)器端關(guān)了,客戶端send就失敗了。 數(shù)據(jù)包丟了就丟了,不會(huì)理會(huì)。不管關(guān)閉哪一端,對(duì)方端都不知道這件事情,彼此無(wú)關(guān)系,無(wú)影響。

如果讓服務(wù)器端一次只接受一個(gè)字符,我給你發(fā)一個(gè)數(shù)據(jù)包,你去收這個(gè)數(shù)據(jù)包,你recvfrom,你把這個(gè)數(shù)據(jù)包拆開(kāi),你讀取1個(gè)字符,后面的不讀,直接就丟掉了。
UDP的報(bào)頭結(jié)構(gòu)
UDP的報(bào)頭固定是8個(gè)字節(jié)!
- UDP的報(bào)文段長(zhǎng)度 – 表示這個(gè)UDP報(bào)文段的報(bào)頭+數(shù)據(jù)部分的總長(zhǎng)度 一個(gè)UDP報(bào)文段數(shù)據(jù)部分的長(zhǎng)度為總長(zhǎng)度 - 8
- 冗余檢驗(yàn)碼 – 會(huì)對(duì)整個(gè)UDP數(shù)據(jù)報(bào)進(jìn)行冗余校驗(yàn)
UDP的優(yōu)勢(shì)
- 沒(méi)有確認(rèn)機(jī)制和超時(shí)重傳機(jī)制,發(fā)送方發(fā)送報(bào)文段的效率就很高。
- 頭部固定部分比較小,一個(gè)UDP報(bào)文段所攜帶的上次協(xié)議的數(shù)據(jù)就比TCP多一點(diǎn)。
- UDP的實(shí)現(xiàn)相對(duì)比較簡(jiǎn)單。
UDP的數(shù)據(jù)報(bào)服務(wù)

- sendto和recvfrom的次數(shù)是一一對(duì)應(yīng)的。
- sendto一次,底層就發(fā)送一個(gè)UDP報(bào)文段,對(duì)方就接受這一個(gè)UDP報(bào)文段。
- 如果一次recvfrom沒(méi)有將一個(gè)UDP報(bào)文段中的數(shù)據(jù)讀取完成,則剩余的數(shù)據(jù)會(huì)被丟棄。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux如何使用 MyCat 實(shí)現(xiàn) MySQL 主從讀寫(xiě)分離
這篇文章主要介紹了Linux如何 MyCat 實(shí)現(xiàn) MySQL 主從讀寫(xiě)分離,感興趣并且想詳情了解的小伙伴接著看下文吧2021-08-08
linux上TCP connection timeout問(wèn)題解決辦法
這篇文章主要介紹了 linux上TCP connection timeout問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
VMWare中CentOS ifcfg-eth0配置方法(親測(cè)直接可用)
本篇文章主要介紹了VMWare中CentOS ifcfg-eth0配置,這些配置是基于VMware Network Adapter VMnet8,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
CentOS系統(tǒng)下Apache配置多域名或多端口映射的方法
我們大多情況是一臺(tái)服務(wù)器一個(gè)IP,這樣配置起來(lái)很簡(jiǎn)單,但是如何想多域名多端口映射的話就沒(méi)那么簡(jiǎn)單了,下面這篇文章主要介紹了CentOS系統(tǒng)下Apache配置多域名或多端口映射的方法,需要的朋友可以參考學(xué)習(xí),下面來(lái)一起看看吧。2016-12-12
詳解Linux如何將一個(gè)文件夾的所有內(nèi)容授權(quán)給某一個(gè)用戶?
這篇文章主要介紹了Linux如何將一個(gè)文件夾的所有內(nèi)容授權(quán)給某一個(gè)用戶,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05

