[socket編程-基于UDP傳輸?shù)睦覿

轉(zhuǎn)自
前面的文章中我們給出了幾個(gè)TCP的例子建车,對(duì)于UDP而言傀顾,只要能理解前面的內(nèi)容丹喻,實(shí)現(xiàn)并非難事薄货。

UDP中的服務(wù)器端和客戶端沒有連接

UDP不像TCP,無需在連接狀態(tài)下交換數(shù)據(jù)碍论,因此基于UDP的服務(wù)器端和客戶端也無需經(jīng)過連接過程谅猾。也就是說,不必調(diào)用 listen() 和 accept() 函數(shù)鳍悠。UDP中只有創(chuàng)建套接字的過程和數(shù)據(jù)交換的過程赊瞬。

UDP服務(wù)器端和客戶端均只需1個(gè)套接字

TCP中先煎,套接字是一對(duì)一的關(guān)系。如要向10個(gè)客戶端提供服務(wù)巧涧,那么除了負(fù)責(zé)監(jiān)聽的套接字外薯蝎,還需要?jiǎng)?chuàng)建10套接字。但在UDP中谤绳,不管是服務(wù)器端還是客戶端都只需要1個(gè)套接字占锯。之前解釋UDP原理的時(shí)候舉了郵寄包裹的例子,負(fù)責(zé)郵寄包裹的快遞公司可以比喻為UDP套接字缩筛,只要有1個(gè)快遞公司消略,就可以通過它向任意地址郵寄包裹。同樣瞎抛,只需1個(gè)UDP套接字就可以向任意主機(jī)傳送數(shù)據(jù)艺演。

基于UDP的接收和發(fā)送函數(shù)

創(chuàng)建好TCP套接字后,傳輸數(shù)據(jù)時(shí)無需再添加地址信息桐臊,因?yàn)門CP套接字將保持與對(duì)方套接字的連接胎撤。換言之,TCP套接字知道目標(biāo)地址信息断凶。但UDP套接字不會(huì)保持連接狀態(tài)伤提,每次傳輸數(shù)據(jù)都要添加目標(biāo)地址信息,這相當(dāng)于在郵寄包裹前填寫收件人地址认烁。

發(fā)送數(shù)據(jù)使用 sendto() 函數(shù):

 //Linux
ssize_t sendto(int sock, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen); 

 //Windows
int sendto(SOCKET sock, const char *buf, int nbytes, int flags, const struct sockadr *to, int addrlen); 

Linux和Windows下的 sendto() 函數(shù)類似肿男,下面是詳細(xì)參數(shù)說明:

  • sock:用于傳輸U(kuò)DP數(shù)據(jù)的套接字;
  • buf:保存待傳輸數(shù)據(jù)的緩沖區(qū)地址却嗡;
  • nbytes:帶傳輸數(shù)據(jù)的長(zhǎng)度(以字節(jié)計(jì))舶沛;
  • flags:可選項(xiàng)參數(shù),若沒有可傳遞0窗价;
  • to:存有目標(biāo)地址信息的 sockaddr 結(jié)構(gòu)體變量的地址如庭;
  • addrlen:傳遞給參數(shù) to 的地址值結(jié)構(gòu)體變量的長(zhǎng)度。

UDP 發(fā)送函數(shù) sendto() 與TCP發(fā)送函數(shù) write()/send() 的最大區(qū)別在于舌镶,sendto() 函數(shù)需要向他傳遞目標(biāo)地址信息。

接收數(shù)據(jù)使用 recvfrom() 函數(shù):

 //Linux
ssize_t recvfrom(int sock, void *buf, size_t nbytes, int flags, struct sockadr *from, socklen_t *addrlen); 

 //Windows
int recvfrom(SOCKET sock, char *buf, int nbytes, int flags, const struct sockaddr *from, int *addrlen); 

由于UDP數(shù)據(jù)的發(fā)送端不不定豪娜,所以 recvfrom() 函數(shù)定義為可接收發(fā)送端信息的形式餐胀,具體參數(shù)如下:

  • sock:用于接收UDP數(shù)據(jù)的套接字;
  • buf:保存接收數(shù)據(jù)的緩沖區(qū)地址瘤载;
  • nbytes:可接收的最大字節(jié)數(shù)(不能超過buf緩沖區(qū)的大蟹裨帧);
  • flags:可選項(xiàng)參數(shù)鸣奔,若沒有可傳遞0墨技;
  • from:存有發(fā)送端地址信息的sockaddr結(jié)構(gòu)體變量的地址惩阶;
  • addrlen:保存參數(shù) from 的結(jié)構(gòu)體變量長(zhǎng)度的變量地址值。
    基于UDP的回聲服務(wù)器端/客戶端

下面結(jié)合之前的內(nèi)容實(shí)現(xiàn)回聲客戶端扣汪。需要注意的是断楷,UDP不同于TCP,不存在請(qǐng)求連接和受理過程崭别,因此在某種意義上無法明確區(qū)分服務(wù)器端和客戶端冬筒,只是因?yàn)槠涮峁┓?wù)而稱為服務(wù)器端,希望各位讀者不要誤解茅主。

下面給出Windows下的代碼舞痰,Linux與此類似,不再贅述诀姚。

服務(wù)器端 server.cpp:

#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //加載 ws2_32.dll
#define BUF_SIZE 100
int main(){
    WSADATA wsaData;
    WSAStartup( MAKEWORD(2, 2), &wsaData);
    //創(chuàng)建套接字
    SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
    //綁定套接字
    sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));  //每個(gè)字節(jié)都用0填充
    servAddr.sin_family = PF_INET;  //使用IPv4地址
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY); //自動(dòng)獲取IP地址
    servAddr.sin_port = htons(1234);  //端口
    bind(sock, (SOCKADDR*)&servAddr, sizeof(SOCKADDR));
    //接收客戶端請(qǐng)求
    SOCKADDR clntAddr;  //客戶端地址信息
    int nSize = sizeof(SOCKADDR);
    char buffer[BUF_SIZE];  //緩沖區(qū)
    while(1){
        int strLen = recvfrom(sock, buffer, BUF_SIZE, 0, &clntAddr, &nSize);
        sendto(sock, buffer, strLen, 0, &clntAddr, nSize);
    }
    closesocket(sock);
    WSACleanup();
    return 0;
}

代碼說明:

  1. 第12行代碼在創(chuàng)建套接字時(shí)响牛,向 socket() 第二個(gè)參數(shù)傳遞 SOCK_DGRAM,以指明使用UDP協(xié)議赫段。

  2. 第18行代碼中使用htonl(INADDR_ANY)來自動(dòng)獲取IP地址呀打。

利用常數(shù) INADDR_ANY 自動(dòng)獲取IP地址有一個(gè)明顯的好處,就是當(dāng)軟件安裝到其他服務(wù)器或者服務(wù)器IP地址改變時(shí)瑞佩,不用再更改源碼重新編譯聚磺,也不用在啟動(dòng)軟件時(shí)手動(dòng)輸入。而且炬丸,如果一臺(tái)計(jì)算機(jī)中已分配多個(gè)IP地址(例如路由器)瘫寝,那么只要端口號(hào)一致,就可以從不同的IP地址接收數(shù)據(jù)稠炬。所以焕阿,服務(wù)器中優(yōu)先考慮使用INADDR_ANY;而客戶端中除非帶有一部分服務(wù)器功能首启,否則不會(huì)采用暮屡。

客戶端 client.cpp:

#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")  //加載 ws2_32.dll
#define BUF_SIZE 100
int main(){
    //初始化DLL
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    //創(chuàng)建套接字
    SOCKET sock = socket(PF_INET, SOCK_DGRAM, 0);
    //服務(wù)器地址信息
    sockaddr_in servAddr;
    memset(&servAddr, 0, sizeof(servAddr));  //每個(gè)字節(jié)都用0填充
    servAddr.sin_family = PF_INET;
    servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    servAddr.sin_port = htons(1234);
    //不斷獲取用戶輸入并發(fā)送給服務(wù)器,然后接受服務(wù)器數(shù)據(jù)
    sockaddr fromAddr;
    int addrLen = sizeof(fromAddr);
    while(1){
        char buffer[BUF_SIZE] = {0};
        printf("Input a string: ");
        gets(buffer);
        sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr*)&servAddr, sizeof(servAddr));
        int strLen = recvfrom(sock, buffer, BUF_SIZE, 0, &fromAddr, &addrLen);
        buffer[strLen] = 0;
        printf("Message form server: %s\n", buffer);
    }
    closesocket(sock);
    WSACleanup();
    return 0;
}

先運(yùn)行 server毅桃,再運(yùn)行 client褒纲,client 輸出結(jié)果為:

Input a string: C語言中文網(wǎng)
Message form server: C語言中文網(wǎng)
Input a string: c.biancheng.net Founded in 2012
Message form server: c.biancheng.net Founded in 2012
Input a string:

從代碼中可以看出,server.cpp 中沒有使用 listen() 函數(shù)钥飞,client.cpp 中也沒有使用 connect() 函數(shù)莺掠,因?yàn)?UDP 不需要連接。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末读宙,一起剝皮案震驚了整個(gè)濱河市彻秆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖唇兑,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酒朵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡扎附,警方通過查閱死者的電腦和手機(jī)蔫耽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帕棉,“玉大人针肥,你說我怎么就攤上這事∠惆椋” “怎么了慰枕?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)即纲。 經(jīng)常有香客問我具帮,道長(zhǎng),這世上最難降的妖魔是什么低斋? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任蜂厅,我火速辦了婚禮,結(jié)果婚禮上膊畴,老公的妹妹穿的比我還像新娘掘猿。我一直安慰自己,他們只是感情好唇跨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布稠通。 她就那樣靜靜地躺著,像睡著了一般买猖。 火紅的嫁衣襯著肌膚如雪改橘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天玉控,我揣著相機(jī)與錄音飞主,去河邊找鬼。 笑死高诺,一個(gè)胖子當(dāng)著我的面吹牛碌识,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播虱而,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼筏餐,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了薛窥?” 一聲冷哼從身側(cè)響起胖烛,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诅迷,沒想到半個(gè)月后佩番,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡罢杉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年趟畏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滩租。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赋秀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出律想,到底是詐尸還是另有隱情猎莲,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布技即,位于F島的核電站著洼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏而叼。R本人自食惡果不足惜身笤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望葵陵。 院中可真熱鬧液荸,春花似錦、人聲如沸脱篙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涡尘。三九已至忍弛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間考抄,已是汗流浹背细疚。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留川梅,地道東北人疯兼。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像贫途,于是被迫代替她去往敵國(guó)和親吧彪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容