上篇博客我們講了,TCP套接字通信這篇博客我們主要講解一下UDP套接字通信。
基本概念
用戶數據報協議UDP(User Datagram Protocol)在傳送數據之前不要要先建立連接,遠地主機的運輸層在收到UDP報文後,不需要給出任何確認。雖然UDP不提供可靠交付,但在某些情況下UDP卻是一種最有效的工作方式。
UDP的主要特點
- UDP是無連接的。
- UDP使用盡最大努力交付。
- UDP是面向報文的。
- UDP沒有擁塞控制
- UDP支持一對一、一對多、多對一和多對多的交互通信
- UDP的首部開銷小,只有8個字節,比TCP的20字節的首部要短
UDP的首部格式
(1)源端口:源端口號,在需要對方回信時選用,不要要時可用全0。
(2)目的端口:目的端口號。在終點交付報文時必須要使用到。
(3)長度:UDP用戶數據報的長度,其最小值是8。
(4)校驗和:檢測UDP用戶數據報在傳輸中是否有錯,有錯就丟棄。
注意:
在計算校驗和時,要在UDP用戶數據報之前增加12個字節的僞首部。所謂“僞首部”是因爲這種僞首部並不是UDP用戶數據報的真正的首部。只是在計算校驗和時,臨時添加在UDP用戶數據報前面,得到一個臨時的UDP用戶數據報。校驗和就是按照這個臨時的UDP用戶數據報來計算的。僞首部既不向下傳送也不向上遞交,而僅僅爲了計算校驗和。
UDP通信的實現方法
UDP通信和上篇的TCP通信實現原理大同小異,但是他們還是有一些差異的:
1. 調用socket函數創建socket的時候,TCP是基於字節流傳輸的,而UDP是基於數據包傳輸的,所以socket函數的第二個參數是SOCK_DGRAM。
2. UDP服務器不需要通過三次握手來建立連接,所以UDP不需要listen去進行監聽。
3. 在綁定之後可以直接進行讀寫,不過不同於TCP,而是自己特有的函數,recvfrom和sendto。
UDP讀寫函數
1.數據發送函數sendto
參數:
sockfd:表示當前的socket的fd。
buf:待發送數據的緩衝區。
size:緩衝區的長度。
falgs:調用方式標誌位,一般位0,改變flags,將會改變Sendto發送的方式
addr:指向目的套接字的地址。
addrlen:所指地址的長度。
2.數據接收函數recvfrom
參數和上面類似,可以參考上面的。
套接字UDP通信
server.c:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void Usage(const char* name)
{
printf("Usage is %s [IP] [port]\n",name);
}
int main(int argc,const char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
//創建套接字 套接字傳輸類型爲數據報傳輸 SOCK_DGRM
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
perror("socket");
return 2;
}
//綁定ip與端口
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
local.sin_addr.s_addr = inet_addr(argv[1]);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
perror("bind");
return 3;
}
//綁定後不需要設置爲監聽狀態,因爲udp是不可靠傳輸,不需要建立連接
char buf[1024];
while(1)
{
//udp客戶端 - 服務器之間的通信採用的是 專門的函數通信
struct sockaddr_in client;
socklen_t len = sizeof(client);
//recvfrom從客戶端接受數據
ssize_t s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct ockaddr*)&client,&len);
if(s < 0)
{
perror("recvfrom");
return 4;
}
else if(s == 0)
{}
else
{
buf[s] = 0;
printf("%s,%d #: %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
//調用sendto發送數據到客戶端
sendto(sock,buf,strlen(buf),0,(struct sockadd*)&client,len);
}
}
close(sock);
return 0;
}
client.c:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void Usage(const char* name)
{
printf("Usage is %s [IP] [port]\n",name);
}
int main(int argc,const char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
//創建套接字 套接字傳輸類型爲數據報傳輸 SOCK_DGRM
int sock = socket(AF_INET,SOCK_DGRAM,0);
if(sock < 0)
{
perror("socket");
return 2;
}
//創建成功
char buf[1024];
printf("Please Enter\n");
while(1)
{
printf("client#: ");
fflush(stdout);
//從標準輸入中讀數據
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
return 3;
}
buf[s-1] = 0;
//給定接受方的協議地址
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(atoi(argv[2]));
peer.sin_addr.s_addr = inet_addr(argv[1]);
//向服務器發送數據
if(sendto(sock,buf,strlen(buf),0,(struct sockaddr*)&peer,sizeof(peer)) < 0)
{
perror("sendto");
return 4;
}
struct sockaddr_in server;
socklen_t len = sizeof(server);
//接受服務器的數據
s = recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&server,&len);
if(s < 0)//接受失敗
{
perror("recvfrom");
return 5;
}
else if(s == 0) //接受到文件結尾,表示服務器關閉
{
printf("server quit!\n");
break;
}
else //接受到的數據放到顯示器上
{
buf[s] = 0;
printf("%s ,%d #:%s\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port),buf);
}
}
close(sock);
return 0;
}
UDP用戶空間如何保證可靠性
UDP它不屬於連接型協議,因而具有資源消耗小,處理速度快的優點,所以通常音頻、視頻和普通數據在傳送時使用UDP較多,因爲它們即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。
傳輸層無法保證數據的可靠傳輸,只能通過應用層來實現了。實現的方式可以參照tcp可靠性傳輸的方式,只是實現不在傳輸層,實現轉移到了應用層。
實現確認機制、重傳機制、窗口確認機制。
如果你不利用Linux協議棧以及上層socket機制,自己通過抓包和發包的方式去實現可靠性傳輸,那麼必須實現如下功能:
發送:包的分片、包確認、包的重發
接收:包的調序、包的序號確認
目前有如下開源程序利用udp實現了可靠的數據傳輸。分別爲RUDP、RTP、UDT。
【RUDP】
RUDP 提供一組數據服務質量增強機制,如擁塞控制的改進、重發機制及淡化服務器算法等,從而在包丟失和網絡擁塞的情況下, RTP 客戶機(實時位置)面前呈現的就是一個高質量的 RTP 流。在不干擾協議的實時特性的同時,可靠 UDP 的擁塞控制機制允許 TCP 方式下的流控制行爲。
【RPT】
實時傳輸協議(RTP)爲數據提供了具有實時特徵的端對端傳送服務,如在組播或單播網絡服務下的交互式視頻音頻或模擬數據。應用程序通常在 UDP 上運行 RTP 以便使用其多路結點和校驗服務;這兩種協議都提供了傳輸層協議的功能。但是 RTP 可以與其它適合的底層網絡或傳輸協議一起使用。如果底層網絡提供組播方式,那麼 RTP 可以使用該組播表傳輸數據到多個目的地。
RTP 本身並沒有提供按時發送機制或其它服務質量(QoS)保證,它依賴於底層服務去實現這一過程。 RTP 並不保證傳送或防止無序傳送,也不確定底層網絡的可靠性。 RTP 實行有序傳送, RTP 中的序列號允許接收方重組發送方的包序列,同時序列號也能用於決定適當的包位置,例如:在視頻解碼中,就不需要順序解碼。、
【UDT】
基於UDP的數據傳輸協議(UDP-basedData Transfer Protocol,簡稱UDT)是一種互聯網數據傳輸協議。UDT的主要目的是支持高速廣域網上的海量數據傳輸,而互聯網上的標準數據傳輸協議TCP在高帶寬長距離網絡上性能很差。顧名思義,UDT建於UDP之上,並引入新的擁塞控制和數據可靠性控制機制。UDT是面向連接的雙向的應用層協議。它同時支持可靠的數據流傳輸和部分可靠的數據報傳輸。由於UDT完全在UDP上實現,它也可以應用在除了高速數據傳輸之外的其它應用領域,例如點到點技術(P2P),防火牆穿透,多媒體數據傳輸等等。
因項目中的需要,現在詳細分析一下UDT是如何通過udp實現數據的可靠傳輸。通過閱讀源碼的方式。
UDT並不是在瓶勁帶寬相對較小的和大量多元短文檔流的情況下用來取代TCP的。
UDT主要作爲TCP的朋友,和TCP並存,UDT分配的帶寬不應該超過根據MAX-MIN規則的最大最小公平共享原則。(備註,最大最小規則允許UDT在高BDP連接下分配TCP不能使用的可用帶寬)。
UDT是雙工的,每個UDT實體有兩個部分:發送和接收。
發送者根據流量控制和速率控制來發送(和重傳)應用程式數據。
接收者接收數據包和控制包,並根據接收到的包發送控制包。發送和接收程式共享同一個UDP端口來發送和接收。
接收者也負責觸發和處理任何的控制事件,包括擁塞控制和可靠性控制和他們的相對機制,例如RTT估計、帶寬估計、應答和重傳。
UDT總是試着將應用層數據打包成固定的大小,除非數據不夠這麼大。和TCP相似的是,這個固定的包大小叫做MSS(最大包大小)。由於期望UDT用來傳輸大塊數據流,我們假定只有很小的一部分不規則的大小的包在UDT session中。MSS能夠通過應用程式來安裝,MTU是其最優值(包括任何包頭)。
UDT擁塞控制算法將速率控制和窗口(流量控制)合併起來,前者調整包的發送週期,後者限制最大的位被應答的包。在速率控制中使用的參數通過帶寬估計技術來更新,他繼承來自基於接收的包方法。同時,速率控制週期是估計RTT的常量,流控制參數依賴於對方的數據到達速度,另外接收端釋放的緩衝區的大小。