Linux下使用C/C++進(jìn)行UDP網(wǎng)絡(luò)編程詳解
UDP 是User Datagram Protocol 的簡(jiǎn)稱,中文名是用戶數(shù)據(jù)報(bào)協(xié)議,是一種無(wú)連接、不可靠的協(xié)議,同樣它也是工作在傳順層。它只是簡(jiǎn)單地實(shí)現(xiàn)從一端主機(jī)到另一端主機(jī)的數(shù)據(jù)傳輸功能,這些數(shù)據(jù)通過(guò) IP 層發(fā)送,在網(wǎng)絡(luò)中傳輸,到達(dá)目標(biāo)主機(jī)的順序是無(wú)法預(yù)知的,因此需要應(yīng)用程序?qū)@些數(shù)據(jù)進(jìn)行排序處理,這就帶來(lái)了很大的不方便,此外,UDP 協(xié)議更沒(méi)有流量控制、擁塞控制等功能,在發(fā)送的一端,UDP 只是把上層應(yīng)用的數(shù)據(jù)封裝到UDP 報(bào)文中,在差錯(cuò)檢測(cè)方面,僅僅是對(duì)數(shù)據(jù)進(jìn)行了簡(jiǎn)單的校驗(yàn),然后將其封裝到 IP 數(shù)據(jù)報(bào)中發(fā)送出去。而在接收端,無(wú)論是否收到數(shù)據(jù),它都不會(huì)產(chǎn)生一個(gè)應(yīng)答發(fā)送給源主機(jī),并且如果接收到數(shù)據(jù)發(fā)送校驗(yàn)錯(cuò)誤,那么接收端就會(huì)丟棄該UDP 報(bào)文,也不會(huì)告訴源主機(jī),這樣子傳輸?shù)臄?shù)據(jù)是無(wú)法保障其準(zhǔn)確性的,如果想要其準(zhǔn)確性,那么就需要應(yīng)用程序來(lái)保障了。
UDP 協(xié)議的特點(diǎn):
- 無(wú)連接、不可靠;
- 盡可能提供交付數(shù)據(jù)服務(wù),出現(xiàn)差錯(cuò)直接丟棄,無(wú)反饋;
- 面向報(bào)文,發(fā)送方的UDP 拿到上層數(shù)據(jù)直接添加個(gè)UDP 首部,然后進(jìn)行校驗(yàn)后就遞交給 IP 層而接收的一方在接收到UDP 報(bào)文后簡(jiǎn)單進(jìn)行校驗(yàn),然后直接去除數(shù)據(jù)遞交給上層應(yīng)用;
- 速度快,因?yàn)閁DP 協(xié)議沒(méi)有TCP 協(xié)議的握手、確認(rèn)、窗口、重傳、擁塞控制等機(jī)制,UDP 是一個(gè)無(wú)狀態(tài)的傳輸協(xié)議,所以它在傳遞數(shù)據(jù)時(shí)非???,即使在網(wǎng)絡(luò)擁塞的時(shí)候UDP 也不會(huì)降低發(fā)送的數(shù)據(jù)。 UDP 雖然有很多缺點(diǎn),但也有自己的優(yōu)點(diǎn),所以它也有很多的應(yīng)用場(chǎng)合,因?yàn)樵谌缃竦木W(wǎng)絡(luò)環(huán)境下, UDP 協(xié)議傳輸出現(xiàn)錯(cuò)誤的概率是很小的,并且它的實(shí)時(shí)性是非常好,常用于實(shí)時(shí)視頻的傳輸,比如直播、網(wǎng)絡(luò)電話等,因?yàn)榧词故浅霈F(xiàn)了數(shù)據(jù)丟失的情況,導(dǎo)致視頻卡幀,這也不是什么大不了的事情,所以,UDP協(xié)議還是會(huì)被應(yīng)用與對(duì)傳輸速度有要求,并且可以容忍出現(xiàn)差錯(cuò)的數(shù)據(jù)傳輸中。
在Linux使用socket網(wǎng)絡(luò)編程實(shí)現(xiàn)udp通信流程如下:
1. 初始化socket
int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); if(sock_fd < 0){ perror("failed to open socket"); return -1; }
2. 綁定IP和端口號(hào)
/** 綁定IP和端口號(hào) */ struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP server_addr.sin_port = htons(8888); // 指定端口號(hào) int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(ret < 0) { perror("failed to bind"); close(sock_fd); return -1; }
3. 設(shè)置組播接收(可選)
std::string multi_addr = "224.0.0.10"; struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str()); mreq.imr_interface.s_addr = INADDR_ANY; ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)); if (0 >ret) { perror("set socket multicast error"); return false; }
4. 設(shè)置接收超時(shí)(可選)
/** 設(shè)置接收超時(shí)(可選) */ int mill_sec = 2000; // 毫秒 struct timeval time_out; time_out.tv_sec = mill_sec / 1000; time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000; ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval)); if(ret < 0) { perror("udp setTimeOut error!"); }
5. 發(fā)送數(shù)據(jù)
unsigned char buf[1024]; std::string ip = "192.168.1.10"; int port = 1234; struct sockaddr_in client{}; memset(&client, 0, sizeof(client)); client.sin_addr.s_addr = inet_addr(ip.c_str()); client.sin_family = AF_INET; client.sin_port = htons(port); ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr)); if(ret < 0){ perror("udpServer send error!"); }else{ std::cout << "send success!" << std::endl; }
6. 接收數(shù)據(jù)
unsigned char buffer[1024]; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); while(true) { memset(buffer, 0, sizeof(buffer)); ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len); if(len > 0) { std::cout << "received message len : " << len << std::endl; }else{ perror("recv error"); } }
7. 完整代碼
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <netdb.h> #include <net/if.h> #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <iostream> #include <cstring> #include <thread> void recv_func(int sock_fd_) { unsigned char buffer[1024]; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); while(true) { memset(buffer, 0, sizeof(buffer)); ssize_t len = recvfrom(sock_fd_, buffer, sizeof(buffer), 0, reinterpret_cast<struct sockaddr *>(&addr), &addr_len); if(len > 0) { std::cout << "received message len : " << len << std::endl; }else{ perror("recv error"); } } } int main(int agrc, char** argv) { int sock_fd = socket(AF_INET , SOCK_DGRAM , 0); if(sock_fd < 0){ perror("failed to open socket"); return -1; } /** 綁定IP和端口號(hào) */ struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; // 本地任意IP server_addr.sin_port = htons(8888); // 指定端口號(hào) int ret = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if(ret < 0) { perror("failed to bind"); close(sock_fd); return -1; } /** 設(shè)置組播接收 (可選)*/ std::string multi_addr = "224.0.0.10"; struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(multi_addr.c_str()); mreq.imr_interface.s_addr = INADDR_ANY; ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)); if (0 >ret) { perror("set socket multicast error"); return false; } /** 設(shè)置接收超時(shí)(可選) */ int mill_sec = 2000; // 毫秒 struct timeval time_out; time_out.tv_sec = mill_sec / 1000; time_out.tv_usec = (mill_sec- time_out.tv_sec * 1000) * 1000; ret = setsockopt(sock_fd, SOL_SOCKET,SO_RCVTIMEO,&time_out,sizeof (timeval)); if(ret < 0) { perror("udp setTimeOut error!"); } /** 開(kāi)啟線程接收 */ std::thread recv_t(recv_func, sock_fd); recv_t.detach(); /** 主線程發(fā)送 */ while (true) { unsigned char buf[1024]; std::string ip = "192.168.1.10"; int port = 1234; struct sockaddr_in client{}; memset(&client, 0, sizeof(client)); client.sin_addr.s_addr = inet_addr(ip.c_str()); client.sin_family = AF_INET; client.sin_port = htons(port); ret = sendto(sock_fd, buf, sizeof(buf), 0, reinterpret_cast<struct sockaddr *>(&client), sizeof(struct sockaddr)); if(ret < 0){ perror("udpServer send error!"); }else{ std::cout << "send success!" << std::endl; } usleep(50*1000); } }
8. 編譯運(yùn)行
# 編譯 g++ udp_main.cpp -o main -lpthread # 運(yùn)行 ./main
以上就是Linux下使用C/C++進(jìn)行UDP網(wǎng)絡(luò)編程詳解的詳細(xì)內(nèi)容,更多關(guān)于C++ UDP網(wǎng)絡(luò)編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
json error: Use of overloaded operator [] is ambiguous錯(cuò)誤的解決方
今天小編就為大家分享一篇關(guān)于json error: Use of overloaded operator [] is ambiguous錯(cuò)誤的解決方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-04-04win10環(huán)境下C++ vs2015編譯opencv249的教程
這篇文章主要介紹了win10環(huán)境下C++ vs2015編譯opencv249的教程,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03函數(shù)外初始化與函數(shù)內(nèi)初始化詳細(xì)解析
函數(shù)內(nèi)初始化:bool FillStr(char *&szDst, int nSize);第一個(gè)參數(shù)中的&一定不能少,這是因?yàn)樵诤瘮?shù)外部我們只聲明了這個(gè)指針,具體這個(gè)指針指向內(nèi)存中的哪個(gè)地址我們并不知道,所以&是為了說(shuō)明傳遞的是這個(gè)指針的引用,那么在函數(shù)內(nèi)初始化后這個(gè)指針的地址也就是外面指針的地址了2013-09-09解析C語(yǔ)言中空指針、空指針常量、NULL & 0的詳解
本篇文章是對(duì)C語(yǔ)言中空指針、空指針常量、NULL & 0 進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++中多態(tài)的定義及實(shí)現(xiàn)詳解
這篇文章主要給大家介紹了關(guān)于C++中多態(tài)的定義及實(shí)現(xiàn)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05詳解C++異常處理(try catch throw)完全攻略
這篇文章主要介紹了詳解C++異常處理(try catch throw)完全攻略,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03C語(yǔ)言雙指針多方法旋轉(zhuǎn)數(shù)組解題LeetCode
這篇文章主要為大家介紹了C語(yǔ)言雙指針使用多方法旋轉(zhuǎn)數(shù)組題解LeetCode,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02C語(yǔ)言連接并操作Sedna XML數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了C語(yǔ)言連接并操作Sedna XML數(shù)據(jù)庫(kù)的方法,實(shí)例分析了C語(yǔ)言操作XML文件的相關(guān)技巧,需要的朋友可以參考下2015-06-06