標籤:網路編程 udp編程 cs
概述
UDP 是 User Datagram Protocol 的簡稱, 中文名是使用者資料包通訊協定,是一個簡單的面向資料報的運輸層協議,在網路中用於處理資料包,是一種不需連線的協議。UDP 不提供可靠性的傳輸,它只是把應用程式傳給 IP 層的資料報發送出去,但是並不能保證它們能到達目的地。由於 UDP 在傳輸資料報前不用在客戶和伺服器之間建立一個串連,且沒有逾時重發等機制,故而傳輸速度很快。
UDP 有如下的特點:
1)郵件系統服務模式的抽象(可通過郵件模型來進行對比)
2)每個分組都攜帶完整的目的地址
3)發送資料之前不需要建立連結
4)不對資料包的順序進行檢查,不能保證分組的先後順序
5)不進行分組出錯的恢複和重傳
6)不保證資料轉送的可靠性
在網路品質令人十分不滿意的環境下,UDP 協議資料包丟失會比較嚴重。但是由於 UDP 的特性:它不屬於串連型協議,因而具有資源消耗小,處理速度快的優點,所以通常音頻、視頻和普通資料在傳送時使用 UDP 較多,因為它們即使偶爾丟失一兩個資料包,也不會對接收結果產生太大影響。比如我們聊天用的 ICQ 和 QQ 就是使用的 UDP 協議。
UDP 編程的 C/S 架構
UDP 用戶端程式對比於寫信模型,用戶端相當於寄信人,要想成功給人寄信,信封上必須寫上對方的地址。
ssize_t sendto(int sockfd,
const void *buf,
size_t nbytes,
int flags,
const struct sockaddr *to,
socklen_t addrlen );
功能:
向 to 結構體指標中指定的 ip,發送 UDP 資料,可以發送 0 長度的 UDP 資料包
參數:
sockfd:通訊端
buf:發送資料緩衝區
nbytes:發送資料緩衝區的大小
flags:一般為 0
to:指向目的主機地址結構體的指標
addrlen:to 所指向內容的長度
返回值:
成功:發送資料的長度
失敗: -1
這裡通過 Windows 的網路調試助手和虛擬機器中的 ubuntu 用戶端程式進行通訊,網路調試助手下載請點此處。
Windows 的網路調試助手作為伺服器,接收用戶端的請求,調試助手配置如下:
虛擬機器中 ubuntu 的 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 = "10.221.20.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 不一樣。
UDP 伺服器程式
UDP網路程式想要收取資料需什麼條件?
1)確定的 ip 地址
2)確定的連接埠(port)
這正如,我要收到別人寄過來的信,我必須告訴別人我的地址(ip),同時告訴別人我我的公寓信箱號(連接埠)。
接收端使用 bind() 函數,來完成地址結構與 socket 通訊端的綁定,這樣 ip、port 就固定了,發送端在 sendto 函數中指定接收端的 ip、port,就可以發送資料了。
需要標頭檔:#include <sys/socket.h>
int bind( int sockfd,
const struct sockaddr *myaddr,
socklen_t addrlen );
功能:
將本地協議地址與 sockfd 綁定,這樣 ip、port 就固定了
參數:
sockfd: socket 通訊端
myaddr: 指向特定協議的地址結構指標
addrlen:該地址結構的長度
返回值:
成功:返回 0
失敗:-1
使用執行個體如下:
// 本網地址struct sockaddr_in my_addr;bzero(&my_addr, sizeof(my_addr));// 清空結構體內容my_addr.sin_family = AF_INET;// ipv4my_addr.sin_port = htons(port);// 連接埠轉換my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 綁定網卡所有ip地址,INADDR_ANY為通配地址,值為0printf("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);}
綁定連接埠有些需要注意的問題,請看《綁定( bind )連接埠需要注意的問題》。
ssize_t recvfrom( int sockfd, void *buf,
size_t nbytes,int flags,
struct sockaddr *from,
socklen_t *addrlen );
功能:
接收 UDP 資料,並將源地址資訊儲存在 from 指向的結構中
參數:
sockfd:通訊端
buf:接收資料緩衝區
nbytes:接收資料緩衝區的大小
flags:通訊端標誌(常為 0)
from:源地址結構體指標,用來儲存資料的來源
addrlen:from 所指內容的長度
返回值:
成功:接收到的長度
失敗: -1
ubuntu 中的伺服器程式如下:
#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);// 連接埠轉換my_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 綁定網卡所有ip地址,INADDR_ANY為通配地址,值為0printf("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;}
Windows 的網路調試助手作為用戶端,給 ubuntu 中的伺服器發送資料,調試助手配置如下:
運行結果如下:
範例程式碼下載請點此處。
Linux 網路編程——UDP編程