襄陽(yáng)手機(jī)網(wǎng)站建設(shè)世界大學(xué)排名
網(wǎng)絡(luò)編程UDP—socket實(shí)現(xiàn)
- 前言
- UDP客戶端和服務(wù)端
- UDP使用場(chǎng)景
- UDP socket C++代碼示例
- 服務(wù)端接收數(shù)據(jù)示例(bind+recvfrom 阻塞式接收信息):
- bind 綁定-監(jiān)聽(tīng) 函數(shù)
- 為什么一般都是監(jiān)聽(tīng)所有網(wǎng)絡(luò)接口呢?
- 為什么需要用inet_addr進(jìn)行轉(zhuǎn)換?
- socket函數(shù)
- sockaddr_in結(jié)構(gòu)體
- recvfrom 函數(shù)
- 客戶端發(fā)送數(shù)據(jù)示例:
- sendto 發(fā)送 函數(shù)
前言
-
UDP通信需要哪些必要信息
- IP地址
- 用于定位通訊雙方
- 端口號(hào)
- 用于標(biāo)識(shí)通信的具體應(yīng)用或服務(wù)。
- 傳輸層通信都需要端口號(hào)的。
- IP地址
-
網(wǎng)絡(luò)要求
- 雙方必須是可以進(jìn)行ip通信的
- UDP依賴IP協(xié)議棧(IPv4或IPv6)完成路由、傳輸
- 雙方需要用同一協(xié)議
- 雙方必須是可以進(jìn)行ip通信的
UDP客戶端和服務(wù)端
-
客戶端
-
構(gòu)造數(shù)據(jù)報(bào):包含目標(biāo)IP、目標(biāo)端口、數(shù)據(jù)內(nèi)容。
-
發(fā)送數(shù)據(jù)報(bào):使用套接字 sendto() 函數(shù)將數(shù)據(jù)發(fā)送到目標(biāo)地址。
-
等待響應(yīng)(如果有):接收服務(wù)端返回的數(shù)據(jù)。
-
服務(wù)端
-
創(chuàng)建監(jiān)聽(tīng)套接字:綁定到指定IP和端口。
-
等待數(shù)據(jù):通過(guò) recvfrom() 函數(shù)接收數(shù)據(jù)。
-
處理請(qǐng)求:解析數(shù)據(jù)內(nèi)容并執(zhí)行相應(yīng)操作。
-
返回響應(yīng):將結(jié)果數(shù)據(jù)發(fā)送回客戶端。
UDP使用場(chǎng)景
UDP適用于以下需要高效傳輸?shù)萑虜?shù)據(jù)丟失的場(chǎng)景:
- 實(shí)時(shí)通信:
- 視頻通話、語(yǔ)音通話(如VoIP)。
- 在線游戲:
- 游戲中快速同步狀態(tài)。
- 流媒體傳輸:
- 實(shí)時(shí)視頻、音頻傳輸。
- 廣播/組播:
- 數(shù)據(jù)包同時(shí)發(fā)送給多個(gè)主機(jī)(如局域網(wǎng)中發(fā)現(xiàn)服務(wù))。
- 輕量級(jí)請(qǐng)求/響應(yīng):
- DNS查詢、簡(jiǎn)單的遠(yuǎn)程控制。
UDP socket C++代碼示例
服務(wù)端接收數(shù)據(jù)示例(bind+recvfrom 阻塞式接收信息):
- 使用場(chǎng)景
- 簡(jiǎn)單服務(wù)端,適用于單個(gè)套接字的接收
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <cstring>int main() {int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); // 創(chuàng)建UDP套接字if (sock_fd < 0) {perror("Socket creation failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080); // 監(jiān)聽(tīng)端口server_addr.sin_addr.s_addr = INADDR_ANY; // 監(jiān)聽(tīng)所有網(wǎng)絡(luò)接口if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("Bind failed");close(sock_fd);return -1;}char buffer[1024];struct sockaddr_in client_addr;socklen_t addr_len = sizeof(client_addr);// 會(huì)阻塞等待 直到 接收信息int bytes_received = recvfrom(sock_fd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &addr_len); // 接收數(shù)據(jù)if (bytes_received > 0) {buffer[bytes_received] = '\0';std::cout << "Received message: " << buffer << std::endl;}close(sock_fd);return 0;
}
bind 綁定-監(jiān)聽(tīng) 函數(shù)
-
功能:
- bind 函數(shù)用于將套接字綁定到特定的IP地址和端口號(hào),通常用于服務(wù)端監(jiān)聽(tīng)套接字。
- 服務(wù)器先運(yùn)行監(jiān)聽(tīng)特定的IP地址和端口號(hào);然后客戶端再
- bind 函數(shù)用于將套接字綁定到特定的IP地址和端口號(hào),通常用于服務(wù)端監(jiān)聽(tīng)套接字。
-
函數(shù)聲明:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
參數(shù)說(shuō)明:
- sockfd:
- 套接字描述符,由 socket 函數(shù)返回。
- addr:
- 指向 sockaddr 結(jié)構(gòu)體,表示要綁定的地址和端口。
- 通常使用 sockaddr_in,需強(qiáng)制轉(zhuǎn)換為 sockaddr。
- 指向 sockaddr 結(jié)構(gòu)體,表示要綁定的地址和端口。
- addrlen:
- addr 的長(zhǎng)度(使用 sizeof(sockaddr_in))。
返回值:
成功:返回 0。
失敗:返回 -1,并設(shè)置 errno。
示例:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080); // 綁定端口號(hào)
server_addr.sin_addr.s_addr = INADDR_ANY; // 綁定到所有可用IP地址if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("Bind failed");close(sock_fd);
}
為什么一般都是監(jiān)聽(tīng)所有網(wǎng)絡(luò)接口呢?
server_addr.sin_addr.s_addr = INADDR_ANY; // 綁定到所有可用IP地址
這行代碼中的 INADDR_ANY 是一個(gè)常用的常量,它代表了一個(gè)特殊的 IP 地址,即 0.0.0.0。當(dāng)你將它設(shè)置為服務(wù)器套接字的地址時(shí),表示該服務(wù)器將 監(jiān)聽(tīng)所有網(wǎng)絡(luò)接口。
-
理解什么是網(wǎng)絡(luò)接口?
- 網(wǎng)絡(luò)接口指計(jì)算機(jī)或設(shè)備上 每一個(gè)可以用于發(fā)送或接收數(shù)據(jù)的網(wǎng)絡(luò)連接通道。
- 可以先理解為網(wǎng)卡,但是網(wǎng)絡(luò)接口還包括一些虛擬網(wǎng)卡、本地回環(huán)接口(127.0.0.1)、甚至 VPN接口等,總的就是軟硬 網(wǎng)絡(luò)通道。
- 網(wǎng)絡(luò)接口指計(jì)算機(jī)或設(shè)備上 每一個(gè)可以用于發(fā)送或接收數(shù)據(jù)的網(wǎng)絡(luò)連接通道。
-
服務(wù)器為什么一般監(jiān)聽(tīng)所有網(wǎng)絡(luò)接口?
- 因?yàn)槔碚撋衔覀兿M灰欠?wù)器這個(gè)端口號(hào)接收的,不管是哪一個(gè)網(wǎng)絡(luò)接口,都交給服務(wù)器應(yīng)用程序處理;
- 除非我們就只想讓服務(wù)器處理從某個(gè)網(wǎng)絡(luò)接口 接收的數(shù)據(jù),才設(shè)置某一個(gè)網(wǎng)絡(luò)接口的ip地址。例如:
- 只想讓服務(wù)器接受本地(同一臺(tái)機(jī)器上的應(yīng)用程序)發(fā)出的數(shù)據(jù)
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
- 只想讓服務(wù)器接受本地(同一臺(tái)機(jī)器上的應(yīng)用程序)發(fā)出的數(shù)據(jù)
為什么需要用inet_addr進(jìn)行轉(zhuǎn)換?
因?yàn)閷?shí)際是采用網(wǎng)絡(luò)字節(jié)序進(jìn)行傳輸?shù)?#xff0c;而用字符串形式十進(jìn)制格式(如 “192.168.1.1”)只是為了人類方便閱讀,所以需要轉(zhuǎn)為網(wǎng)絡(luò)字節(jié)序。
-
inet_addr作用
- 用于將一個(gè)點(diǎn)分十進(jìn)制表示的 IPv4 地址(例如 “192.168.1.1”)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的二進(jìn)制格式。
-
inet_addr 將一個(gè) IPv4 地址的點(diǎn)分十進(jìn)制字符串(如 “192.168.1.1”)轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的 32 位整數(shù)。例如:
- “192.168.1.1” 在十進(jìn)制中是:192 * 256^3 + 168 * 256^2 + 1 * 256^1 + 1 * 256^0
- 對(duì)應(yīng)的二進(jìn)制表示是:11000000 10101000 00000001 00000001
-
值得注意的是,inet_addr 已經(jīng)不推薦使用,特別是在現(xiàn)代網(wǎng)絡(luò)編程中,因?yàn)樗鼘?duì)無(wú)效地址的處理可能不夠清晰(比如返回 -1 會(huì)被誤認(rèn)為是有效的地址)。推薦使用 inet_pton 函數(shù)來(lái)替代,它更加健壯和安全。inet_pton 允許支持不同的地址族(IPv4 和 IPv6),并且不會(huì)出現(xiàn)類似 inet_addr 那樣的錯(cuò)誤返回值。
socket函數(shù)
-
作用
- 用于創(chuàng)建套接字socket,套接字是網(wǎng)絡(luò)通信的基礎(chǔ),用于在客戶端和服務(wù)端之間建立通信。
- 確定協(xié)議族(IPv4還是)、TCP還是UDP
-
函數(shù)聲明
int socket(int domain, int type, int protocol);
參數(shù)說(shuō)明:
- domain:指定通信的協(xié)議族(地址類型)。
- AF_INET:IPv4。
- AF_INET6:IPv6。
- AF_UNIX:本地通信(不使用網(wǎng)絡(luò))。
- type:指定套接字的類型。
- SOCK_STREAM:TCP(面向連接,保證數(shù)據(jù)可靠性)。
- SOCK_DGRAM:UDP(無(wú)連接,適合快速傳輸)。
- protocol:通常指定為 0,表示使用默認(rèn)協(xié)議。
- 如果 type 是 SOCK_DGRAM,默認(rèn)使用 UDP 協(xié)議。
- 如果 type 是 SOCK_STREAM,默認(rèn)使用 TCP 協(xié)議。
返回值:
成功:返回套接字描述符(非負(fù)整數(shù))。
失敗:返回 -1,并設(shè)置 errno。
示例:
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd < 0) {perror("Socket creation failed");
}
sockaddr_in結(jié)構(gòu)體
-
功能
- 用于表示 IPv4 地址和端口信息,通常在網(wǎng)絡(luò)通信中用于綁定或指定目標(biāo)地址。
-
定義:
struct sockaddr_in {short sin_family; // 地址族(必須為 AF_INET)unsigned short sin_port; // 端口號(hào)(網(wǎng)絡(luò)字節(jié)序,需要使用 htons() 轉(zhuǎn)換)struct in_addr sin_addr; // IPv4 地址char sin_zero[8]; // 填充字節(jié),保持與 struct sockaddr 的大小一致(不使用,置 0)
};
字段說(shuō)明:
- sin_family:
- 必須設(shè)置為 AF_INET(IPv4協(xié)議)。
- sin_port:
- 16位端口號(hào),必須用 htons() 將主機(jī)字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序。
- sin_addr:
- 一個(gè) struct in_addr 結(jié)構(gòu)體,表示IPv4地址。
- 可以用 inet_addr() 或 inet_aton() 轉(zhuǎn)換字符串形式的IP地址。
- 也可以設(shè)置為 INADDR_ANY,表示綁定到本地所有可用IP。
- 一個(gè) struct in_addr 結(jié)構(gòu)體,表示IPv4地址。
- sin_zero:
- 填充字段,不使用,應(yīng)設(shè)置為 0。
示例:
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET; // IPv4
server_addr.sin_port = htons(8080); // 端口號(hào)(轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序)
server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); // 目標(biāo)IP地址
memset(server_addr.sin_zero, 0, sizeof(server_addr.sin_zero)); // 填充為0
recvfrom 函數(shù)
- 作用:一個(gè)用于從套接字接收數(shù)據(jù)的函數(shù)。
- 函數(shù)聲明:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
- sockfd:要讀取數(shù)據(jù)的套接字文件描述符。
- buf:指向接收數(shù)據(jù)的緩沖區(qū)。
- len:緩沖區(qū)的大小。如果接收的數(shù)據(jù)超過(guò)該大小,數(shù)據(jù)會(huì)被截?cái)唷?/li>
- flags:設(shè)置標(biāo)志,通常為 0。
控制接收行為的標(biāo)志。常用的標(biāo)志包括:- MSG_PEEK:查看數(shù)據(jù)但不從隊(duì)列中移除數(shù)據(jù)。
- MSG_WAITALL:接收指定大小的完整數(shù)據(jù),直到所有數(shù)據(jù)都接收到才返回。
- MSG_DONTWAIT:非阻塞操作,如果沒(méi)有數(shù)據(jù)可接收則立即返回。
- MSG_TRUNC:如果接收的消息太大,超過(guò)緩沖區(qū)的大小,將丟棄多余部分并返回 EMSGSIZE 錯(cuò)誤。
- src_addr:接收數(shù)據(jù)源的地址,通??梢詾?NULL,如果不需要知道源地址。
- 創(chuàng)建一個(gè)指針,用來(lái)接收數(shù)據(jù)源的地址
- addrlen:地址長(zhǎng)度,如果 src_addr 不是NULL,它將被修改為實(shí)際的地址長(zhǎng)度。
函數(shù)返回值:
- 成功時(shí):返回接收到的字節(jié)數(shù)。如果沒(méi)有數(shù)據(jù)到達(dá),且沒(méi)有設(shè)置非阻塞標(biāo)志,則會(huì)阻塞直到有數(shù)據(jù)可讀;如果設(shè)置了 MSG_DONTWAIT 或套接字為非阻塞模式,它將立即返回 0 表示沒(méi)有數(shù)據(jù)。
- 失敗時(shí):返回 -1,并設(shè)置 errno 以指示錯(cuò)誤。常見(jiàn)錯(cuò)誤包括:
- EAGAIN 或 EWOULDBLOCK:非阻塞模式下沒(méi)有數(shù)據(jù)可接收。
- EBADF:sockfd 不是有效的套接字。
- ECONNREFUSED:目標(biāo)主機(jī)拒絕連接(僅在某些類型的套接字中出現(xiàn))。
- EINVAL:無(wú)效的地址長(zhǎng)度或參數(shù)。
客戶端發(fā)送數(shù)據(jù)示例:
#include <iostream>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <cstring>int main() {int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); // 創(chuàng)建UDP套接字if (sock_fd < 0) {perror("Socket creation failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(8080); // 目標(biāo)端口號(hào)server_addr.sin_addr.s_addr = inet_addr("192.168.1.1"); // 目標(biāo)IP地址const char* message = "Hello, UDP!";sendto(sock_fd, message, strlen(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 發(fā)送數(shù)據(jù)close(sock_fd);return 0;
}
sendto 發(fā)送 函數(shù)
-
功能:
- 用于通過(guò)UDP套接字發(fā)送數(shù)據(jù)報(bào)到指定地址和端口
-
函數(shù)聲明
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
參數(shù)說(shuō)明:
- sockfd:
- 套接字描述符,由 socket 函數(shù)返回。
- buf:
- 指向要發(fā)送的數(shù)據(jù)的緩沖區(qū)。
- len:
- 要發(fā)送的數(shù)據(jù)長(zhǎng)度(字節(jié)數(shù))。
- flags:
- 傳輸標(biāo)志,通常設(shè)置為 0。
- dest_addr:
- 指向一個(gè) sockaddr 結(jié)構(gòu)體,表示目標(biāo)地址。
- 通常傳入 sockaddr_in,需要通過(guò)強(qiáng)制類型轉(zhuǎn)換為 sockaddr。
- 指向一個(gè) sockaddr 結(jié)構(gòu)體,表示目標(biāo)地址。
- addrlen:
- dest_addr 的長(zhǎng)度(使用 sizeof(sockaddr_in))。
返回值:
成功:返回實(shí)際發(fā)送的字節(jié)數(shù)。
失敗:返回 -1,并設(shè)置 errno。
- dest_addr 的長(zhǎng)度(使用 sizeof(sockaddr_in))。
示例:
const char *message = "Hello, UDP!";
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.1");sendto(sock_fd, message, strlen(message), 0, (struct sockaddr*)&server_addr, sizeof(server_addr));