Linux網(wǎng)絡(luò)編程系列? (夠吃,管飽)
? ? ? ? 1、Linux網(wǎng)絡(luò)編程系列之網(wǎng)絡(luò)編程基礎(chǔ)
? ? ? ? 2、Linux網(wǎng)絡(luò)編程系列之TCP協(xié)議編程
? ? ? ? 3、Linux網(wǎng)絡(luò)編程系列之UDP協(xié)議編程
? ? ? ? 4、Linux網(wǎng)絡(luò)編程系列之UDP廣播
? ? ? ? 5、Linux網(wǎng)絡(luò)編程系列之UDP組播
? ? ? ? 6、Linux網(wǎng)絡(luò)編程系列之服務(wù)器編程——阻塞IO模型
? ? ? ? 7、Linux網(wǎng)絡(luò)編程系列之服務(wù)器編程——非阻塞IO模型
? ? ? ? 8、Linux網(wǎng)絡(luò)編程系列之服務(wù)器編程——多路復(fù)用模型
? ? ? ? 9、Linux網(wǎng)絡(luò)編程系列之服務(wù)器編程——信號(hào)驅(qū)動(dòng)模型
一、什么是UDP廣播
???????UDP廣播是一種網(wǎng)絡(luò)通信的方式,在廣域網(wǎng)或局域網(wǎng)中,UDP廣播可以向多個(gè)目標(biāo)主機(jī)發(fā)送數(shù)據(jù)包,使得網(wǎng)絡(luò)中的所有設(shè)備都能接收到廣播消息。一定是采用UDP協(xié)議。
二、特性
? ? ? ? 1、面向無(wú)連接:UDP廣播不需要建立連接,可以直接發(fā)送數(shù)據(jù)包到目標(biāo)設(shè)備。
? ? ? ? 2、廣播特性:UDP廣播可以向一個(gè)網(wǎng)絡(luò)中的所有設(shè)備發(fā)送數(shù)據(jù)包。
? ? ? ? 3、不可靠性:UDP廣播發(fā)送的數(shù)據(jù)包無(wú)法保證傳輸?shù)目煽啃?,可能?huì)發(fā)生數(shù)據(jù)丟失、錯(cuò)誤等情況。
? ? ? ? 4、速度快、開銷小:UDP廣播不需要建立連接,因此傳輸速度快,開銷小,適用于實(shí)時(shí)流媒體傳輸?shù)葢?yīng)用場(chǎng)景。
? ? ? ? 5、?安全性較低:UDP廣播發(fā)送的數(shù)據(jù)包可以被網(wǎng)絡(luò)中的其他設(shè)備接收,可能會(huì)存在數(shù)據(jù)泄露的風(fēng)險(xiǎn)。
? ? ? ? 6、適用于廣播通信場(chǎng)景:UDP廣播適用于需要向網(wǎng)絡(luò)中所有設(shè)備發(fā)送數(shù)據(jù)的場(chǎng)景,比如尋找可用設(shè)備、傳輸實(shí)時(shí)視頻或音頻數(shù)據(jù)等。
三、使用場(chǎng)景
????????UDP廣播主要用于數(shù)據(jù)的實(shí)時(shí)傳輸和設(shè)備的發(fā)現(xiàn),常見的應(yīng)用場(chǎng)景包括:
? ? ? ? 1、視頻和音頻的實(shí)時(shí)傳輸:UDP廣播可以使得多個(gè)設(shè)備同時(shí)接收到同一流的數(shù)據(jù),實(shí)現(xiàn)實(shí)時(shí)的視頻會(huì)議和音頻播放等功能。
? ? ? ? 2、網(wǎng)絡(luò)打印機(jī)的自動(dòng)發(fā)現(xiàn):通過UDP廣播,打印機(jī)可以向網(wǎng)絡(luò)中廣播自己的存在,從而被所有的設(shè)備發(fā)現(xiàn)和使用。
? ? ? ? 3、多人游戲的聯(lián)機(jī):UDP廣播可以將游戲數(shù)據(jù)同時(shí)發(fā)送給所有玩家的設(shè)備,實(shí)現(xiàn)多人游戲的聯(lián)機(jī)功能。
? ? ? ? 4、網(wǎng)絡(luò)攝像頭的實(shí)時(shí)監(jiān)控:通過UDP廣播,攝像頭可以將實(shí)時(shí)的視頻流發(fā)送給所有監(jiān)控軟件,使得監(jiān)控人員能夠同時(shí)查看視頻。
四、UDP廣播通信流程
? ? ?? ?1、發(fā)送方(不一定是服務(wù)器或者客戶端)
? ? ? ? (1)、建立套接字。使用socket()
? ? ? ? (2)、設(shè)置套接字屬性為廣播。使用setsockopt()
? ? ? ? (3)、綁定自己的IP地址和端口號(hào)。使用bind()(可以省略)
? ? ? ? (4)、發(fā)送數(shù)據(jù),并指定接收方為廣播地址。使用sendto()
? ? ? ? (5)、關(guān)閉套接字
? ? ? ? 2、接收方(不一定是服務(wù)器或者客戶端)
????????(1)、建立套接字。使用socket()
? ? ? ? (2)、設(shè)置端口復(fù)用。使用setsockopt()(可選,推薦)
? ? ? ? (3)、綁定IP地址為廣播地址和端口號(hào)。使用bind()(不可以省略)
? ? ? ? (4)、接收數(shù)據(jù)。使用recvfrom()
? ? ? ? (5)、關(guān)閉套接字
五、相關(guān)函數(shù)API
?? 1、建立套接字
// 建立套接字 int socket(int domain, int type, int protocol); // 接口說明 返回值:成功返回一個(gè)套接字文件描述符,失敗返回-1 參數(shù)domain:用來(lái)指定使用何種地址類型,有很多,具體看別的資源 (1)PF_INET 或者 AF_INET 使用IPV4網(wǎng)絡(luò)協(xié)議 (2)其他很多的,看別的資源 參數(shù)type:通信狀態(tài)類型選擇,有很多,具體看別的資源 (1)SOCK_STREAM 提供雙向連續(xù)且可信賴的數(shù)據(jù)流,即TCP (2)SOCK_DGRAM 使用不連續(xù)不可信賴的數(shù)據(jù)包連接,即UDP 參數(shù)protocol:用來(lái)指定socket所使用的傳輸協(xié)議編號(hào),通常不用管,一般設(shè)為0
?? ????????2、設(shè)置端口狀態(tài)
// 設(shè)置端口的狀態(tài) int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); // 接口說明 返回值:成功返回0,失敗返回-1 參數(shù)sockfd:待設(shè)置的套接字 參數(shù)level: 待設(shè)置的網(wǎng)絡(luò)層,一般設(shè)成為SOL_SOCKET以存取socket層 參數(shù)optname:待設(shè)置的選項(xiàng),有很多種,具體看別的資源,這里講常用的 (1)、SO_REUSEADDR 允許在bind()過程中本地地址可復(fù)用,即端口復(fù)用 (2)、SO_BROADCAST 使用廣播的方式發(fā)送,通常用于UDP廣播 (3)、SO_SNDBUF 設(shè)置發(fā)送的暫存區(qū)大小 (4)、SO_RCVBUF 設(shè)置接收的暫存區(qū)大小 參數(shù)optval:待設(shè)置的值 參數(shù)optlen:參數(shù)optval的大小,即sizeof(optval)
? ????????3、綁定IP地址和端口號(hào)
// 綁定自己的IP地址和端口號(hào) int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // 接口說明 返回值: 參數(shù)sockfd:待綁定的套接字 參數(shù)addrlen:參數(shù)addr的大小,即sizeof(addr) 參數(shù)addr:IP地址和端口的結(jié)構(gòu)體,通用的結(jié)構(gòu)體,根據(jù)sockfd的類型有不同的定義 當(dāng)sockfd的domain參數(shù)指定為IPV4時(shí),結(jié)構(gòu)體定義為 struct sockaddr_in { unsigned short int sin_family; // 需與sockfd的domain參數(shù)一致 uint16_t sin_port; // 端口號(hào) struct in_addr sin_addr; // IP地址 unsigned char sin_zero[8]; // 保留的,未使用 }; struct in_addr { uin32_t s_addr; } // 注意:網(wǎng)絡(luò)通信時(shí),采用大端字節(jié)序,所以端口號(hào)和IP地址需要調(diào)用專門的函數(shù)轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
?????????? 4、字節(jié)序轉(zhuǎn)換接口?
// 第一組接口 // 主機(jī)轉(zhuǎn)網(wǎng)絡(luò)IP地址,輸入主機(jī)IP地址 uint32_t htonl(uint32_t hostlong); // 主機(jī)轉(zhuǎn)網(wǎng)絡(luò)端口,輸入主機(jī)端口號(hào) uint16_t htons(uint16_t hostshort); // 常用 // 網(wǎng)絡(luò)轉(zhuǎn)主機(jī)IP,輸入網(wǎng)絡(luò)IP地址 uint32_t ntohl(uint32_t netlong); // 網(wǎng)絡(luò)轉(zhuǎn)主機(jī)端口,輸入網(wǎng)絡(luò)端口 uint16_t ntohs(uint16_t netshort); // 第二組接口,只能用于IPV4轉(zhuǎn)換,IP地址 // 主機(jī)轉(zhuǎn)網(wǎng)絡(luò) int inet_aton(const char *cp, struct in_addr *inp); // 主機(jī)轉(zhuǎn)網(wǎng)絡(luò) in_addr_t inet_addr(const char *cp); // 常用 // 網(wǎng)絡(luò)轉(zhuǎn)主機(jī) int_addr_t inet_network(const char *cp); // 網(wǎng)絡(luò)轉(zhuǎn)主機(jī) char *inet_ntoa(struct in_addr in); // 常用
????????? 5、發(fā)送數(shù)據(jù)
// UDP協(xié)議發(fā)送數(shù)據(jù) ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); // 接口說明 返回值:成功返回成功發(fā)送的字節(jié)數(shù),失敗返回-1 參數(shù)sockfd:發(fā)送者的套接字 參數(shù)buf:發(fā)送的數(shù)據(jù)緩沖區(qū) 參數(shù)len:發(fā)送的長(zhǎng)度 參數(shù)flags:一般設(shè)置為0,還有其他數(shù)值,具體查詢別的資源 參數(shù)dest_addr:接收者的網(wǎng)絡(luò)地址 參數(shù)addrlen:接收者的網(wǎng)絡(luò)地址大小,即sizeof(dest_addr)
?? ????????6、接收數(shù)據(jù)
// UDP協(xié)議接收數(shù)據(jù) ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); // 接口說明: 返回值:成功返回成功接收的字節(jié)數(shù),失敗返回-1 參數(shù)sockfd:接收者的套接字 參數(shù)buf:接收數(shù)據(jù)緩的沖區(qū) 參數(shù)len:接收的最大長(zhǎng)度 參數(shù)flags:一般設(shè)置為0,還有其他數(shù)值,具體查詢別的資源 參數(shù)src_addr:發(fā)送者的網(wǎng)絡(luò)地址,可以設(shè)置為NULL 參數(shù)addrlen: 發(fā)送者的網(wǎng)絡(luò)地址大小,即sizeof(src_addr)
????????? 7、關(guān)閉套接字
// 關(guān)閉套接字 int close(int fd); // 接口說明 返回值:成功返回0,失敗返回-1 參數(shù)fd:套接字文件描述符
六、案例
? ? ? ? 實(shí)現(xiàn)UDP廣播的演示
? ? ? ? 發(fā)送方BroadcastSend.c
// UDP廣播發(fā)送方的案例 #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #define SEND_IP "192.168.64.128" // 記得改為自己IP #define SEND_PORT 10000 // 不能超過65535,也不要低于1000,防止端口誤用 int main(int argc, char *argv[]) { // 1、建立套接字,使用IPV4網(wǎng)絡(luò)地址,UDP協(xié)議 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd == -1) { perror("socket fail"); return -1; } // 2、設(shè)置套接字為廣播屬性 int optval = 1; // 這里設(shè)置套接字為廣播屬性,所以隨便寫一個(gè)值 int ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); if(ret == -1) { perror("setsockopt fail"); close(sockfd); return -1; } // 3、綁定自己的IP地址和端口號(hào)(可以省略) struct sockaddr_in send_addr = {0}; socklen_t addr_len = sizeof(struct sockaddr); send_addr.sin_family = AF_INET; // 指定協(xié)議為IPV4地址協(xié)議 send_addr.sin_port = htons(SEND_PORT); // 端口號(hào) send_addr.sin_addr.s_addr = inet_addr(SEND_IP); // IP地址 ret = bind(sockfd, (struct sockaddr*)&send_addr, addr_len); if(ret == -1) { perror("bind fail"); close(sockfd); return -1; } // 4、發(fā)送數(shù)據(jù) uint16_t port = 0; // 端口號(hào) char ip[20] = {0}; // IP地址 struct sockaddr_in recv_addr = {0}; char msg[128] = {0}; // 數(shù)據(jù)緩沖區(qū) // 注意輸入廣播地址,格式為*.*.*.255 printf("please input receiver IP and port\n"); scanf("%s %hd", ip, &port); printf("IP = %s, port = %hd\n", ip, port); recv_addr.sin_family = AF_INET; // 指定用IPV4地址 recv_addr.sin_port = htons(port); // 接收者的端口號(hào) recv_addr.sin_addr.s_addr = inet_addr(ip); // 接收者的IP地址 while(getchar() != '\n'); // 清空多余的換行符 while(1) { printf("please input data:\n"); fgets(msg, sizeof(msg)/sizeof(msg[0]), stdin); // 發(fā)送數(shù)據(jù),注意要填寫接收者的地址 ret = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&recv_addr, addr_len); if(ret > 0) { printf("success: send %d bytes\n", ret); } } // 5、關(guān)閉套接字 close(sockfd); return 0; }
? ? ? ? 接收方BroadcastRecv.c?
// UDP廣播接收方的案例 #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #define RECV_IP "192.168.64.255" // 記得改為廣播地址 #define RECV_PORT 20000 // 不能超過65535,也不要低于1000,防止端口誤用 int main(int argc, char *argv[]) { // 1、建立套接字,使用IPV4網(wǎng)絡(luò)地址,UDP協(xié)議 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd == -1) { perror("socket fail"); return -1; } // 2、設(shè)置端口復(fù)用(推薦) int optval = 1; // 這里設(shè)置為端口復(fù)用,所以隨便寫一個(gè)值 int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); if(ret == -1) { perror("setsockopt fail"); close(sockfd); return -1; } // 3、綁定自己的IP地址和端口號(hào)(不可以省略) struct sockaddr_in recv_addr = {0}; socklen_t addr_len = sizeof(struct sockaddr); recv_addr.sin_family = AF_INET; // 指定協(xié)議為IPV4地址協(xié)議 recv_addr.sin_port = htons(RECV_PORT); // 端口號(hào) // recv_addr.sin_addr.s_addr = inet_addr(RECV_IP); // IP地址. 寫下面的更好 recv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本機(jī)內(nèi)所有的IP地址 ret = bind(sockfd, (struct sockaddr*)&recv_addr, addr_len); if(ret == -1) { perror("bind fail"); close(sockfd); return -1; } // 4、接收數(shù)據(jù) uint16_t port = 0; // 端口號(hào) char ip[20] = {0}; // IP地址 struct sockaddr_in send_addr = {0}; char msg[128] = {0}; // 數(shù)據(jù)緩沖區(qū) while(1) { // 接收數(shù)據(jù),注意使用發(fā)送者的地址來(lái)接收 ret = recvfrom(sockfd, msg, sizeof(msg)/sizeof(msg[0]), 0, (struct sockaddr*)&send_addr, &addr_len); if(ret > 0) { memset(ip, 0, sizeof(ip)); // 先清空IP strcpy(ip, inet_ntoa(send_addr.sin_addr)); // 網(wǎng)絡(luò)IP轉(zhuǎn)主機(jī)IP port = ntohs(send_addr.sin_port); // 網(wǎng)絡(luò)端口號(hào)轉(zhuǎn)主機(jī)端口號(hào) printf("[%s:%d] send data: %s\n", ip, port, msg); memset(msg, 0, sizeof(msg)); // 清空數(shù)據(jù)區(qū) } } // 5、關(guān)閉套接字 close(sockfd); return 0; }
? ? ? ? 通信演示
? ? ? ? 注:第一幅圖由于只有一臺(tái)電腦不太好演示廣播效果,第二幅圖用了一臺(tái)電腦和一個(gè)開發(fā)板。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-767691.html
七、總結(jié)
? ? ? ? UDP廣播一定是采用UDP協(xié)議的,通信流程跟UDP協(xié)議的通信流程差不多,就是要注意設(shè)置發(fā)送方套接字屬性為廣播,然后設(shè)置接收方的IP地址為廣播地址,UDP廣播主要用于數(shù)據(jù)的實(shí)時(shí)傳輸和設(shè)備的發(fā)現(xiàn)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-767691.html
到了這里,關(guān)于Linux網(wǎng)絡(luò)編程系列之UDP廣播的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!