做網站用lunx鏈接交換公司
- (??? ),Hello我是祐言QAQ
- 我的博客主頁:C/C++語言,數(shù)據結構,Linux基礎,ARM開發(fā)板,網絡編程等領域UP🌍
- 快上🚘,一起學習,讓我們成為一個強大的攻城獅!
- 送給自己和讀者的一句雞湯🤔:集中起來的意志可以擊穿頑石!
- 作者水平很有限,如果發(fā)現(xiàn)錯誤,請在評論區(qū)指正,感謝🙏
????????在計算機網絡中,UDP(User Datagram Protocol)是一種重要的傳輸層協(xié)議,與TCP(Transmission Control Protocol)一樣位于 OSI 模型的傳輸層。但與TCP不同,UDP提供了一種無連接、輕量級的數(shù)據傳輸方式,適用于需要快速傳輸數(shù)據的應用場景。本文將深入探討UDP協(xié)議的特點、用途以及與TCP的對比。
一、UDP的特點
-
無連接性:UDP不需要在通信前建立連接,也不維護連接狀態(tài),因此它更加輕量級。這使得UDP適用于實時數(shù)據傳輸,如音頻和視頻流。
-
不可靠性:UDP不提供數(shù)據包的可靠性傳輸。這意味著數(shù)據包可能會丟失、重復或無序到達目標。因此,UDP通常用于那些可以容忍一些數(shù)據丟失的應用,如實時多媒體流。
-
高性能:由于不需要建立連接和維護狀態(tài)信息,UDP的開銷較低,具有較高的性能。這使得它成為一種適用于高吞吐量、低延遲的協(xié)議。
-
頭部小:UDP的頭部相對較小,只包含源端口、目標端口、長度和校驗和等字段,這有助于減少數(shù)據傳輸時的開銷。
-
多播和廣播:UDP支持多播和廣播,可以同時向多個接收方發(fā)送數(shù)據包,適用于一對多或多對多通信。
二、UDP的用途
????????UDP在許多應用中發(fā)揮著重要作用,包括但不限于:
-
實時音視頻傳輸:VoIP電話、視頻會議和直播流都使用UDP來傳輸實時音視頻數(shù)據,因為它具有低延遲和快速傳輸?shù)奶匦浴?/p>
-
DNS查詢:域名系統(tǒng)(DNS)使用UDP來快速查詢域名解析。
-
游戲數(shù)據傳輸:在線游戲經常使用UDP來傳輸游戲數(shù)據,以確保低延遲和實時性。
-
網絡廣播:UDP用于發(fā)送廣播消息,例如局域網內的設備發(fā)現(xiàn)和服務廣告。
三、UDP實現(xiàn)服務器與客戶端相互通信
? ? ? ? 在此之前我們已經學習掌握了TCP/IP實現(xiàn)服務器與客戶端通信,鏈接我放這里:
【網絡編程】TCP傳輸控制協(xié)議(Transmission Control Protocol)_祐言QAQ的博客-CSDN博客
????????那么現(xiàn)在讓我們一起來學習一下UDP如何實現(xiàn)通信。
1.UDP服務器與客戶端設計思路
????????不難看出其中與TCP通信有幾個不同點:
①套接字類型:
????????TCP使用 SOCK_STREAM 表示流式套接字,這意味著它提供面向連接的、可靠的、基于字節(jié)流的通信;
????????UDP使用 SOCK_DGRAM 表示數(shù)據報套接字,這表明它提供無連接的、不可靠的、基于數(shù)據報的通信。
②通信流程:
????????在TCP中,通信需要經過 listen、accept 和 connect 過程,其中建立連接是必要的;
????????在UDP中,通信是無連接的,不需要建立連接,因此不需要進行 listen、accept 和 connect 步驟。
③收發(fā)數(shù)據的函數(shù):
????????在TCP中,通常使用 recv 和 send 函數(shù)來進行數(shù)據的接收和發(fā)送;
????????在UDP中,通常使用 recvfrom 和 sendto 函數(shù)來進行數(shù)據的接收和發(fā)送。這些函數(shù)需要指定目標地址,因為UDP是無連接的,每個數(shù)據包都需要包含目標地址信息。
2.具體實現(xiàn)代碼
udp_server.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 定義一個結構體用于存儲客戶端信息
struct Client
{struct sockaddr_in addr; // 客戶端地址結構體int ser_socket; // 服務器套接字
};// 線程函數(shù),用于發(fā)送數(shù)據
void *send_data(void *arg)
{struct Client *cli = (struct Client *)arg;char buf[1024];while (1){scanf("%[^\n]", buf); // 從用戶輸入讀取數(shù)據while (getchar() != '\n');// 發(fā)送數(shù)據到客戶端sendto(cli->ser_socket, buf, strlen(buf), 0, (struct sockaddr *)&(cli->addr), sizeof(cli->addr));}
}int main(int argc, char const *argv[])
{if (argc != 2){printf("./server <port>\n");return -1;}// 創(chuàng)建套接字socketint ser_socket = socket(AF_INET, SOCK_DGRAM, 0);if (ser_socket == -1){perror("socket");return -1;}// 設置套接字屬性,SO_REUSEADDR 允許地址端口重用int on = 1;if (setsockopt(ser_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){perror("setsockopt");return -1;}// 初始化地址結構體struct sockaddr_in addr;addr.sin_family = AF_INET; // 地址簇addr.sin_port = atoi(argv[1]); // 端口(一般以傳參的傳進來)// addr.sin_addr.s_addr = inet_addr("192.168.1.128"); // IP地址addr.sin_addr.s_addr = htonl(INADDR_ANY); // 用特殊的"0.0.0.0"這個IP來綁定本機IP地址// bind 綁定IP跟PORTint b = bind(ser_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));if(b == -1){perror("bind");return -1;}printf("綁定成功\n");struct sockaddr_in c_addr; // IPV4地址結構體int addrlen = sizeof(c_addr);char buf[1024];// 接收客戶端的第一條消息recvfrom(ser_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);struct Client cli;cli.addr = c_addr;cli.ser_socket = ser_socket;// 創(chuàng)建一個線程用來發(fā)送數(shù)據pthread_t pid;pthread_create(&pid, NULL, send_data, &cli);// 接收/發(fā)送數(shù)據 recvfrom/sendtowhile(1){bzero(buf, sizeof(buf));// 每接收一條客戶端發(fā)送的信息,保存一次客戶端的IP+PORTrecvfrom(ser_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);// 第一次接收,創(chuàng)建線程發(fā)送數(shù)據,將套接字,對方的IP地址傳遞給線程任務函數(shù)printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);}// 關閉套接字closeclose(ser_socket);return 0;
}
udp_client.c
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>// 線程函數(shù),用于接收數(shù)據
void *recv_data(void *arg)
{int cli_socket = *(int *)arg;struct sockaddr_in c_addr;int addrlen = sizeof(c_addr);char buf[1024];while(1){bzero(buf, sizeof(buf));// 每接收一條客戶端發(fā)送的信息,保存一次客戶端的IP+PORTrecvfrom(cli_socket, buf, sizeof(buf), 0, (struct sockaddr *)&c_addr, &addrlen);printf("[%s] [%d]:%s\n", inet_ntoa(c_addr.sin_addr), c_addr.sin_port, buf);}
}int main(int argc, char const *argv[])
{if (argc != 3){printf("./client <ip> <port>\n");return -1;}// (1) 創(chuàng)建套接字socketint cli_socket = socket(AF_INET, SOCK_DGRAM, 0);if (cli_socket == -1){perror("socket");return -1;}// (2) 初始化地址結構體(服務器的)struct sockaddr_in addr;addr.sin_family = AF_INET; // 地址簇addr.sin_port = atoi(argv[2]); // 服務器端的端口(一般以傳參的傳進來)addr.sin_addr.s_addr = inet_addr(argv[1]); // 服務器端的IP地址(一般以傳參的傳進來)// 先發(fā)一條上線的消息給serverchar buf[1024] = "on line";sendto(cli_socket, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));// 創(chuàng)建線程用來接收數(shù)據pthread_t pid;pthread_create(&pid, NULL, recv_data, &cli_socket);// (3) 發(fā)送數(shù)據while(1){scanf("%[^\n]", buf); // 從用戶輸入讀取數(shù)據while(getchar()!='\n');// 發(fā)送數(shù)據bufsendto(cli_socket, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));}// (5) 關閉套接字closeclose(cli_socket);return 0;
}
四、UDP套接字獲取和設置
????????在UDP套接字編程中,你可以使用 getsockopt
和 setsockopt
函數(shù)來獲取和設置套接字的屬性。以下是一些常見的UDP套接字屬性以及如何使用這兩個函數(shù)來處理它們:
獲取套接字屬性(使用 getsockopt
函數(shù)):
-
SO_RCVBUF 和 SO_SNDBUF:
- 功能:獲取套接字的接收緩沖區(qū)和發(fā)送緩沖區(qū)的大小。
- 示例代碼:
int buffer_size; socklen_t optlen = sizeof(buffer_size); getsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, &optlen); // 現(xiàn)在 buffer_size 中包含接收緩沖區(qū)的大小
-
SO_RCVTIMEO 和 SO_SNDTIMEO:
- 功能:獲取套接字的接收超時時間和發(fā)送超時時間。
- 示例代碼:
struct timeval timeout; socklen_t optlen = sizeof(timeout); getsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, &optlen); // 現(xiàn)在 timeout 中包含接收超時時間
設置套接字屬性(使用 setsockopt
函數(shù)):
-
SO_RCVBUF 和 SO_SNDBUF:
- 功能:設置套接字的接收緩沖區(qū)和發(fā)送緩沖區(qū)的大小。
- 示例代碼:
int buffer_size = 8192; // 設置緩沖區(qū)大小 setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &buffer_size, sizeof(buffer_size));
-
SO_RCVTIMEO 和 SO_SNDTIMEO:
- 功能:設置套接字的接收超時時間和發(fā)送超時時間。
- 示例代碼:
struct timeval timeout; timeout.tv_sec = 5; // 設置超時時間為5秒 timeout.tv_usec = 0; setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
????????這些是一些常見的UDP套接字屬性設置選項??梢愿鶕枰褂?strong> getsockopt
和 setsockopt
函數(shù)來獲取和設置套接字的其他屬性,具體選項取決于你的應用程序的需求。套接字屬性設置允許你自定義套接字的行為,以滿足不同的網絡通信需求。在上面的服務端與客戶端通信中也有用到一些示例。
五、與TCP的對比
UDP和TCP是兩種不同的傳輸協(xié)議,它們在以下方面有所不同:
-
連接性:UDP無連接,TCP面向連接。
-
可靠性:UDP不提供可靠性傳輸,TCP提供可靠性傳輸。
-
開銷:UDP開銷較低,TCP開銷較高。
-
適用場景:UDP適用于需要快速傳輸?shù)梢匀萑桃恍?shù)據丟失的應用,而TCP適用于需要確保數(shù)據完整性和可靠性的應用。
六、總結
????????UDP協(xié)議在網絡通信中扮演著重要的角色,尤其是在需要實時性和低延遲的應用中。雖然它不提供可靠性傳輸,但在正確的應用場景下,UDP是一個強大的工具,能夠滿足快速數(shù)據傳輸?shù)男枨?。了解UDP的特點和用途有助于網絡工程師更好地選擇合適的協(xié)議來滿足應用程序的需求。
????????更多C/C++語言、Linux系統(tǒng)、數(shù)據結構和ARM板實戰(zhàn)相關文章,關注專欄:
? ?手撕C語言
? ? ? ? ? ? 玩轉linux
????????????????????腳踢數(shù)據結構
? ? ? ? ? ? ? ? ? ? ? ? ? ? 系統(tǒng)、網絡編程
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?探索C++
??? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? 6818(ARM)開發(fā)板實戰(zhàn)
📢寫在最后
- 今天的分享就到這啦~
- 覺得博主寫的還不錯的煩勞?
一鍵三連喔
~ - 🎉🎉🎉感謝關注🎉🎉🎉