【Linux网络编程】UDP编程
00. 目錄
文章目錄
- 00. 目錄
- 01. 概述
- 02. UDP編程C/S結構
- 03. UDP常用函數
- 3.1 sendto函數
- 3.2 recvfrom函數
- 3.3 bind函數
- 04. 程序示例
- 4.1 UDP客戶端
- 4.2 UDP服務端
- 05. 附錄
01. 概述
UDP 是 User Datagram Protocol 的簡稱, 中文名是用戶數據報協議,是一個簡單的面向數據報的傳輸層協議,在網絡中用于處理數據包,是一種無連接的協議。UDP 不提供可靠性的傳輸,它只是把應用程序傳給 IP 層的數據報發送出去,但是并不能保證它們能到達目的地。由于 UDP 在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,故而傳輸速度很快。
UDP 有如下的特點:
1)郵件系統服務模式的抽象(可通過郵件模型來進行對比)
2)每個分組都攜帶完整的目的地址
3)發送數據之前不需要建立鏈接
4)不對數據包的順序進行檢查,不能保證分組的先后順序
5)不進行分組出錯的恢復和重傳
6)不保證數據傳輸的可靠性
在網絡質量令人十分不滿意的環境下,UDP 協議數據包丟失會比較嚴重。但是由于 UDP 的特性:它不屬于連接型協議,因而具有資源消耗小,處理速度快的優點,所以通常音頻、視頻和普通數據在傳送時使用 UDP 較多,因為它們即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。比如我們聊天用的 ICQ 和 QQ 就是使用的 UDP 協議。
02. UDP編程C/S結構
對于 UDP客戶端編程流程,有點類似于寫信過程:找個郵政工作人員( socket() )->信封上寫上地址同時里面裝上信件內容并且投遞(sendto() )-> ……還可以繼續寫信,或者,接收對方的回信(recvfrom() )……-> 打完收工(close() )。
對于 UDP 服務器編程流程, 有點類似于收信過程:找個郵政工作人員( socket() ) -> 確定信箱的位置:地址+信箱號(bind() )-> 等待對方的來信( recvfrom() )-> ……還可以回信(write() ),或者,繼續等待對方的來信……
UDP 服務器程序
UDP網絡程序想要收取數據需什么條件?
1)確定的 ip 地址
2)確定的端口(port)
這正如,我要收到別人寄過來的信,我必須告訴別人我的地址(ip),同時告訴別人我我的公寓信箱號(端口)。
接收端使用 bind() 函數,來完成地址結構與 socket 套接字的綁定,這樣 ip、port 就固定了,發送端在 sendto 函數中指定接收端的 ip、port,就可以發送數據了。
03. UDP常用函數
3.1 sendto函數
#include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *to, socklen_t addrlen); 功能:向 to 結構體指針中指定的 ip,發送 UDP 數據,可以發送 0 長度的 UDP 數據包 參數:sockfd:套接字buf:發送數據緩沖區len:發送數據緩沖區的大小flags:一般為 0to:指向目的主機地址結構體的指針addrlen:to 所指向內容的長度 返回值:成功:發送數據的長度失敗: -13.2 recvfrom函數
#include <sys/types.h> #include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen); 功能:接收 UDP 數據,并將源地址信息保存在 from 指向的結構中,默認的情況下,如果沒有接收到數據,這個函數會阻塞,直到有數據到來。 參數:sockfd:套接字buf:接收數據緩沖區len:接收數據緩沖區的大小flags:套接字標志(常為 0)from:源地址結構體指針,用來保存數據的來源addrlen:from 所指內容的長度 返回值:成功:接收到的長度失敗: -13.3 bind函數
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 功能:將本地協議地址與 sockfd 綁定,這樣 ip、port 就固定了 參數:sockfd:socket 套接字addr: 指向特定協議的地址結構指針addrlen:該地址結構的長度 返回值:成功:返回 0失敗:-1參考示例:
// 本地網絡地址 struct sockaddr_in my_addr; bzero(&my_addr, sizeof(my_addr)); // 清空結構體內容 my_addr.sin_family = AF_INET; // ipv4 my_addr.sin_port = htons(port); // 端口轉換 // 綁定網卡所有ip地址,INADDR_ANY為通配地址,值為0 my_addr.sin_addr.s_addr = htonl(INADDR_ANY); printf("Binding server to port %d\n", port); int err_log; err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr)); // 綁定 if(err_log != 0) {perror("bind");close(sockfd); exit(-1); }04. 程序示例
4.1 UDP客戶端
對比于寫信模型,客戶端相當于寄信人,要想成功給人寄信,信封上必須寫上對方的地址。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>int main(int argc, char *argv[]) {unsigned short port = 8080; //服務器端口char *server_ip = "192.168.12.10"; //服務器ip地址if( argc > 1 ) // main函數傳參,服務器ip地址{ server_ip = argv[1];}if( argc > 2 ) // main函數傳參,服務器端口{port = atoi(argv[2]);}int sockfd;sockfd = socket(AF_INET, SOCK_DGRAM, 0); //創建UDP套接字if(sockfd < 0){perror("socket");exit(-1);}// 套接字地址struct sockaddr_in dest_addr;bzero(&dest_addr, sizeof(dest_addr)); // 清空內容dest_addr.sin_family = AF_INET; // ipv4dest_addr.sin_port = htons(port); // 端口轉換inet_pton(AF_INET, server_ip, &dest_addr.sin_addr); // ip地址轉換printf("send data to UDP server %s:%d!\n", server_ip, port);while(1){char send_buf[512] = "";fgets(send_buf, sizeof(send_buf), stdin);//獲取輸入send_buf[strlen(send_buf)-1] = '\0';//發送數據int len = sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));printf("len = %d\n", len);}close(sockfd);return 0; }UDP客戶端注意事項:
1)本地IP、本地端口(我是誰)
2)目的IP、目的端口(發給誰)
3)在客戶端的代碼中,我們只設置了目的IP、目的端口
4)客戶端的本地 ip、本地 port 是我們調用 sendto 的時候 linux 系統底層自動給客戶端分配的;分配端口的方式為隨機分配,即每次運行系統給的 port 不一樣。
4.2 UDP服務端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char *argv[]) {unsigned short port = 8000; // 本地端口if(argc > 1){port = atoi(argv[1]);}int sockfd;sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 創建套接字if(sockfd < 0){perror("socket");exit(-1);}// 本地網絡地址struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr)); // 清空結構體內容my_addr.sin_family = AF_INET; // ipv4my_addr.sin_port = htons(port); // 端口轉換// 綁定網卡所有ip地址,INADDR_ANY為通配地址,值為0my_addr.sin_addr.s_addr = htonl(INADDR_ANY); printf("Binding server to port %d\n", port);int err_log;err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr)); // 綁定if(err_log != 0){perror("bind");close(sockfd); exit(-1);}printf("receive data...\n");while(1){int recv_len;char recv_buf[512] = "";struct sockaddr_in client_addr;char cli_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16socklen_t cliaddr_len = sizeof(client_addr); // 接受數據recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&client_addr, &cliaddr_len);inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf("\nip:%s ,port:%d\n",cli_ip, ntohs(client_addr.sin_port));printf("data(%d):%s\n",recv_len,recv_buf);}close(sockfd);return 0; }05. 附錄
總結
以上是生活随笔為你收集整理的【Linux网络编程】UDP编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux网络编程】套接字简介
- 下一篇: linux 其他常用命令