1. UDP簡介
UDP 和TCP 的區別包括 1. 面向字節流和面向報文 2. TCP必須要建立連接后才能進行數據交換,但是UDP則並沒有連接的建立和釋放過程。面向字節流說明,tcp報文段(segment)是沒有邊界的,當服務器發送多個報文段到客戶端時,客戶端可能會把着多個報文段合並成一個報文段進行接收。但是對於UDP來說,數據是通過報文段進行傳輸的。
如果說TCP協議可以用打電話去比喻,那么UDP協議可以用郵箱來形容。用郵箱傳遞信息時,雖然有丟失的可能性。但是用戶1發送的信件和用戶2接受的信件肯定是相同的。
2. 用UDP編寫echo server
使用UDP,我們只需要修改一下socket函數的參數就OK了。
int client_sock = socket(PF_INET, SOCK_DGRAM, 0);
然后就是創建兩個表示客戶端和服務端地址的sockaddr_in結構,其中serv_addr的各個字段需要我們自己填充。但是client_addr的各個字段是通過recvfrom通過指針進行自動填充。
struct sockaddr_in serv_addr, client_addr;
填充serv_addr的各個字段:
serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(8080);
下來使用bind函數將套接字和服務器地址進行綁定。
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { err_exit("bind error"); }
數據傳輸方面使用recv族和sendto進行數據傳輸,這里值得注意的是只有sendto中的sockaddr_in結構是需要手動填充的,sendto中的地址表示要發送的目標地址。recvfrom中的是獲取數據的源地址。下圖是echo服務器的交互流程。

3. 地址長度所遇到的一個坑
int sLen = sendto(serv_sock, buffer, rLen, MSG_CONFIRM, (struct sockaddr *)&client_addr, recv_addr_len);
client_addr是通過服務端調用recvfrom獲取的,我心想這也不用我手動填啊,怎么就又錯了。又反復看了書上的代碼,確定地址的獲取方式沒有錯,通過打印我發現地址確實有問題,端口和地址都不對。在網上搜了下 udp echo server的代碼,用可以正常運行的代碼和我的代碼細細比對,終於發現了問題所在。問題就出在地址長度的初始化上,我一直以為recvfrom中的地址長度是值-返回模式,但是實際上這個值也是要在recvfrom中起作用的。如果這個值不正確,client_addr的值也是錯誤的,導致服務器無法正常的把數據發送到客戶端。就醬。。。
socklen_t recv_addr_len = sizeof(client_addr); //正確的初始化方式 //socklen_t recv_addr_len; //錯誤的初始化方式 int rLen = recvfrom(serv_sock, buffer, MAX_LEN, MSG_WAITALL, (struct sockaddr *)&client_addr, &recv_addr_len);