Linux之UDP協(xié)議及其編程全流程
UDP協(xié)議的特點
UDP 不提供可靠性的傳輸,它只是把應(yīng)用程序傳給 IP 層的數(shù)據(jù)報發(fā)送出去,但是并不能保證它們能到達(dá)目的地。
由于 UDP 在傳輸數(shù)據(jù)報前不用在客戶和服務(wù)器之間建立一個連接,且沒有超時重發(fā)等機制,故而傳輸速度很快。
- 無連接
- 不可靠
- 數(shù)據(jù)報服務(wù)
UDP發(fā)出的數(shù)據(jù)包不經(jīng)過確認(rèn),可以繼續(xù)發(fā)送。發(fā)送成功與否都不管,盡最大能力去發(fā)送,丟包也不負(fù)責(zé)。有自己的使用特點:適合于做視頻(實時性)適合于即使丟包了,處理起來也比較方便。
適合于攝像頭以恒定速率發(fā),對方以恒定速率收,丟包了繼續(xù)發(fā),可以實時。
但是如果是TCP,如果丟包,會重發(fā),時間花銷大了,不能實時。不適合做攝像頭和視頻。
UDP的編程流程
UDP接口原型
接收
int recvfrom(int sockfd,void *buf,size_t size,int flag,struct sockaddr *peer_addr,socklen_t *addr_len);
peer_addr
:用來保存recvfrom接收到的數(shù)據(jù)是來自哪臺主機的地址信息addr_len
:地址結(jié)構(gòu)的長度
發(fā)送
int sendto(int sockfd,void *buf,size_t size,int flag,struct sockaddr *peer_addr,socklen_t addr_len);
peer_addr
:用來指定數(shù)據(jù)的接收方的地址信息addr_len
:地址信息的長度
示例代碼
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; //將主機字節(jié)序轉(zhuǎn)化為網(wǎng)絡(luò)字節(jié)序 ser_addr.sin_port = htons(6000); //將點分十進(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("請輸入:"); 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ù)器地址信息已知,無需保存直接傳入NULL if(m <= 0) { break; } printf("%s\n",buff); } close(sockfd); exit(0); }
兩個客戶端同時向服務(wù)器端發(fā)送信息
多個客戶端可以和服務(wù)器一起鏈接通訊。recvfrom并不是只等第一個或者第二個客戶端,而是誰給它發(fā),它就收誰的。
如果在客戶端保持運行狀態(tài)的情況下,將服務(wù)器端關(guān)閉,然后再把服務(wù)器端重新運行起來,這時候客戶端發(fā)送數(shù)據(jù),服務(wù)器端是可以收到的。
因為UDP本來就沒有建立連接。如果服務(wù)器端關(guān)了,客戶端send就失敗了。 數(shù)據(jù)包丟了就丟了,不會理會。不管關(guān)閉哪一端,對方端都不知道這件事情,彼此無關(guān)系,無影響。
如果讓服務(wù)器端一次只接受一個字符,我給你發(fā)一個數(shù)據(jù)包,你去收這個數(shù)據(jù)包,你recvfrom,你把這個數(shù)據(jù)包拆開,你讀取1個字符,后面的不讀,直接就丟掉了。
UDP的報頭結(jié)構(gòu)
UDP的報頭固定是8個字節(jié)!
- UDP的報文段長度 – 表示這個UDP報文段的報頭+數(shù)據(jù)部分的總長度 一個UDP報文段數(shù)據(jù)部分的長度為總長度 - 8
- 冗余檢驗碼 – 會對整個UDP數(shù)據(jù)報進(jìn)行冗余校驗
UDP的優(yōu)勢
- 沒有確認(rèn)機制和超時重傳機制,發(fā)送方發(fā)送報文段的效率就很高。
- 頭部固定部分比較小,一個UDP報文段所攜帶的上次協(xié)議的數(shù)據(jù)就比TCP多一點。
- UDP的實現(xiàn)相對比較簡單。
UDP的數(shù)據(jù)報服務(wù)
- sendto和recvfrom的次數(shù)是一一對應(yīng)的。
- sendto一次,底層就發(fā)送一個UDP報文段,對方就接受這一個UDP報文段。
- 如果一次recvfrom沒有將一個UDP報文段中的數(shù)據(jù)讀取完成,則剩余的數(shù)據(jù)會被丟棄。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Linux (Ubuntu)休眠,掛起,待機,關(guān)機的命令詳細(xì)介紹
這篇文章主要介紹了Linux 休眠,掛起,待機,關(guān)機的命令詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-10-10centos7下安裝oracle11gR2的詳細(xì)步驟
本篇文章主要介紹了centos7下安裝oracle11gR2的詳細(xì)步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02Linux 查看磁盤IO并找出占用IO讀寫很高的進(jìn)程
這篇文章主要介紹了Linux 查看磁盤IO并找出占用IO讀寫很高的進(jìn)程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02