介紹UDP協議,并提供一個適用于客戶端和服務器端的實例子程序。
關鍵詞:Linux;UDP協議;TCP/IP協議;程序設計
UDP Protocol Program Based on Linux
LIU Chang, PENG Chuwu
(School of Electrical Engineering, Hunan University, Changsha 410082,China)
Key words: Linux; UDP protocol; TCP/IP protocol; programming
UDP(User Datagram Protocol)是一個面向數據報的簡單傳輸層協議,它為應用程序發送和接收數據報。它是一種無連接協議,即它不像TCP那樣需要建立服務器與客戶端的連接才可以工作。
UDP是個很簡單的協議,它建立在IP協議之上,利用IP數據報提供一種無連接的高效率的服務。它不保證數據的可靠,不進行重傳,時延較短,很適用實時性要求高而不需要數據絕對可靠的應用。在網絡通信質量較好的情況下,UDP體現出高效率,這適合于傳送少量報文的應用,其可靠性由應用程序來保證,如:接收信號后向源方返回一個回響,超時重發、數據檢驗等功能需應用程序來實現。UDP不提供流控制,它按發送方的速率發送數據,而不管接收方的緩存區大小,這樣容易造成溢出錯誤。由于UDP不存在客戶端與服務器的連接,它可用同一個套接字向不同的目標發送數據報;同樣,UDP可以是雙向的,所以也可通過同一個套接字從幾個不同的發送源接收數據報。?
UDP是個很簡單的協議,它建立在IP協議之上,利用IP數據報提供一種無連接的高效率的服務。它不保證數據的可靠,不進行重傳,時延較短,很適用實時性要求高而不需要數據絕對可靠的應用。在網絡通信質量較好的情況下,UDP體現出高效率,這適合于傳送少量報文的應用,其可靠性由應用程序來保證,如:接收信號后向源方返回一個回響,超時重發、數據檢驗等功能需應用程序來實現。UDP不提供流控制,它按發送方的速率發送數據,而不管接收方的緩存區大小,這樣容易造成溢出錯誤。由于UDP不存在客戶端與服務器的連接,它可用同一個套接字向不同的目標發送數據報;同樣,UDP可以是雙向的,所以也可通過同一個套接字從幾個不同的發送源接收數據報。?
linux系統是通過套接字來進行網絡編程的。下面介紹幾個UDP數據報編程用到的基本套接字函數:
網絡程序通過socket和其他幾個函數調用,會返回一個用于通信的套接字描述符。Linux應用程序在執行任何形式的I/O的時候,程序是在讀或者寫一個文件描述符。因此,我們可以將創建的套接字描述符看成普通文件的描述符來操作,并可以通過向套接字描述符讀寫操作實現網絡之間的數據交流,這就是linux設備無關性的好處。
(1)int socket(int domain,int type,int protocol)
函數socket()用于創建一個套接字描述符。參數domain說明網絡程序所在的主機采用的通信協議(AFUNIX和AFINET等)。AFUNIX只能用于單一的UNIX系統進程間通信,而AFINET是針對Internet的,因而可以允許在遠程主機之間通信。一般把它賦為AFINET。參數type指明創建的套接字類型,對應的參數值為SOCKDGRAM:數據報套接字,表明用的是UDP協議,提供無序的、不可靠的、無連接的通信。參數protocol,由于指定了type,所以這里一般只要用0來代替即可。?
socket()得到套接字描述符,為網絡通信做基本的準備工作,調用成功時將返回文件描述符,失敗時將返回-1,通過查看error文件可以知道有關出錯的詳細信息。
(2)int bind(int sockfd,struct sockaddr*myaddr,int addrlen)
得到套接字描述符后,將套接口和機器上的一定的端口號綁定在一起。sockfd是調用socket函數返回的文件描述符;addrlen是sockaddr結構的長度:myaddr是一個指向sockaddr結構的指針,它保存著本地套接字的地址(即端口和IP地址)信息。不過由于系統兼容性的問題,一般不使用這個結構,而使用另外一個結構(struct sockaddrin)來代替。bind()函數在成功被調用時返回0,如果出錯返回-1。
結構類型struct sockaddrin保存著套接口的地址信息,定義如下:
struct sockaddrin{short int sinfamily;/*地址族*/
unsigned short int sinport;/*端口號*/
struct inaddr sinaddr;/*IP地址*/
unsigned char sinzero[8];}/*填充0以保持與struct sockaddr同樣大小*/
這個數據結構使得使用其中的各個元素更為方便,sinzero(它用來將sockaddrin結構填充到與sockaddr結構同樣的長度)應該用bzero()或memset()函數將其置為0。另外,一個指向sockaddrin數據結構的指針可以強行轉換為一個指向數據結構sockaddr的指針,反之亦然。
(3)int close(int sockfd)
函數close用來關閉一個套接字描述符,參數sockfd為指定要關閉的套接字描述符,函數close()在成功執行時返回0,否則返回-1。
以上三個函數中,前兩個要包含頭文件#include〈sys/types.h〉和#include〈sys/socket.h〉,后一個包含#include〈unistd.h〉。再介紹兩個用于UDP協議的接收和發送函數,包含頭文件#include〈sys/socket.h〉:
int sendto(int sockfd,const void*msg,int len,unsigned int flags,struct

3利用UDP數據報編程
UDP協議有自己的通信模型。首先由UDP服務器調用函數socket創建一個數據報類型的套接字,然后服務器調用bind函數綁定在一個默認的UDP端口上,接著調用函數recvfrom在指定的端口上等待UDP客戶機數據報的到來。而UDP客戶機首先也調用函數socket創建一個數據報類型的套接字,然后調用函數sendto向UDP服務器發送數據報。需要注意的是,UDP客戶機應用程序通常不需要調用函數bind將套接字綁定到某個固定的端口,linux操作系統會為進程分配一個空閑的端口號。UDP服務器進程接收到客戶機發來的數據報后,將從recvfrom函數中返回,對數據報進行相關處理后,再調用sendto函數將結果返回到客戶端。
通常,客戶端的設計和實現比服務器端的要容易一些,典型的UDP服務器與操作系統進行交互作用,而且大多數需要同時處理多個客戶。一個UDP客戶機啟動后直接與單個服務器通信,然后就結束了。而對于服務器來說,它啟動后處于休眠狀態,等待客戶請求的到來。當客戶數據報到達時,服務器蘇醒過來,數據報中可能包含來自客戶的某種形式的請求消息。一個基本的UDP通信過程如圖1所示。
網絡程序通過socket和其他幾個函數調用,會返回一個用于通信的套接字描述符。Linux應用程序在執行任何形式的I/O的時候,程序是在讀或者寫一個文件描述符。因此,我們可以將創建的套接字描述符看成普通文件的描述符來操作,并可以通過向套接字描述符讀寫操作實現網絡之間的數據交流,這就是linux設備無關性的好處。
(1)int socket(int domain,int type,int protocol)
函數socket()用于創建一個套接字描述符。參數domain說明網絡程序所在的主機采用的通信協議(AFUNIX和AFINET等)。AFUNIX只能用于單一的UNIX系統進程間通信,而AFINET是針對Internet的,因而可以允許在遠程主機之間通信。一般把它賦為AFINET。參數type指明創建的套接字類型,對應的參數值為SOCKDGRAM:數據報套接字,表明用的是UDP協議,提供無序的、不可靠的、無連接的通信。參數protocol,由于指定了type,所以這里一般只要用0來代替即可。?
socket()得到套接字描述符,為網絡通信做基本的準備工作,調用成功時將返回文件描述符,失敗時將返回-1,通過查看error文件可以知道有關出錯的詳細信息。
(2)int bind(int sockfd,struct sockaddr*myaddr,int addrlen)
得到套接字描述符后,將套接口和機器上的一定的端口號綁定在一起。sockfd是調用socket函數返回的文件描述符;addrlen是sockaddr結構的長度:myaddr是一個指向sockaddr結構的指針,它保存著本地套接字的地址(即端口和IP地址)信息。不過由于系統兼容性的問題,一般不使用這個結構,而使用另外一個結構(struct sockaddrin)來代替。bind()函數在成功被調用時返回0,如果出錯返回-1。
結構類型struct sockaddrin保存著套接口的地址信息,定義如下:
struct sockaddrin{short int sinfamily;/*地址族*/
unsigned short int sinport;/*端口號*/
struct inaddr sinaddr;/*IP地址*/
unsigned char sinzero[8];}/*填充0以保持與struct sockaddr同樣大小*/
這個數據結構使得使用其中的各個元素更為方便,sinzero(它用來將sockaddrin結構填充到與sockaddr結構同樣的長度)應該用bzero()或memset()函數將其置為0。另外,一個指向sockaddrin數據結構的指針可以強行轉換為一個指向數據結構sockaddr的指針,反之亦然。
(3)int close(int sockfd)
函數close用來關閉一個套接字描述符,參數sockfd為指定要關閉的套接字描述符,函數close()在成功執行時返回0,否則返回-1。
以上三個函數中,前兩個要包含頭文件#include〈sys/types.h〉和#include〈sys/socket.h〉,后一個包含#include〈unistd.h〉。再介紹兩個用于UDP協議的接收和發送函數,包含頭文件#include〈sys/socket.h〉:
int sendto(int sockfd,const void*msg,int len,unsigned int flags,struct

3利用UDP數據報編程
UDP協議有自己的通信模型。首先由UDP服務器調用函數socket創建一個數據報類型的套接字,然后服務器調用bind函數綁定在一個默認的UDP端口上,接著調用函數recvfrom在指定的端口上等待UDP客戶機數據報的到來。而UDP客戶機首先也調用函數socket創建一個數據報類型的套接字,然后調用函數sendto向UDP服務器發送數據報。需要注意的是,UDP客戶機應用程序通常不需要調用函數bind將套接字綁定到某個固定的端口,linux操作系統會為進程分配一個空閑的端口號。UDP服務器進程接收到客戶機發來的數據報后,將從recvfrom函數中返回,對數據報進行相關處理后,再調用sendto函數將結果返回到客戶端。
通常,客戶端的設計和實現比服務器端的要容易一些,典型的UDP服務器與操作系統進行交互作用,而且大多數需要同時處理多個客戶。一個UDP客戶機啟動后直接與單個服務器通信,然后就結束了。而對于服務器來說,它啟動后處于休眠狀態,等待客戶請求的到來。當客戶數據報到達時,服務器蘇醒過來,數據報中可能包含來自客戶的某種形式的請求消息。一個基本的UDP通信過程如圖1所示。




4結束語
本文介紹了UDP套接字的通信機制。UDP是一個面向數據報的簡單傳輸層協議。建議網絡環境好,要求資源少的采用UDP,如很多實時系統一般采用UDP協議進行數據的網絡傳輸。如果需要對接收的UDP數據報進行排序和流量控制,并保證數據報的可靠性, 則必須在應用程序中增加一些控制機制。
評論