《UNIX网络编程 卷1:套接字联网API》学习笔记——基本UDP套接字编程
UNIX網(wǎng)絡(luò)編程——基本UDP套接字編程
- 概述
- recvfrom 和 sendto函數(shù)
- UDP 回射服務(wù)器程序:main 函數(shù)
- UDP 回射服務(wù)器程序:dg_echo 函數(shù)
- UDP 回射客戶程序: main 函數(shù)
- UDP 回射客戶程序: dg_cli 函數(shù)
- 數(shù)據(jù)報(bào)的丟失
- 驗(yàn)證接收到的響應(yīng)
概述
UDP是無連接不可靠的數(shù)據(jù)報(bào)協(xié)議。
使用UDP編寫的一些常見的應(yīng)用程序有:DNS(域名系統(tǒng))、NFS(網(wǎng)絡(luò)文件系統(tǒng))和 SNMP(簡單網(wǎng)絡(luò)管理協(xié)議)。
recvfrom 和 sendto函數(shù)
#include <sys/socket.h> ssize_t recvfrom(int sockfd, void* buff, size_t nbytes, int flags, struct sockaddr* from, socklen_t *addrlen); ssize_t sendto(int sockfd, const void* buff, size_t nbytes, int flags,const struct sockaddr* to, socklen_t addrlen);均返回:若成功則為讀或?qū)懙淖止?jié)數(shù),若出錯(cuò)則為-1前三個(gè)參數(shù) sockfd、buff 和 nbytes 等同于 read 和 write 函數(shù)的三個(gè)參數(shù):描述符、指向讀入或?qū)懗删彌_區(qū)的指針和讀寫字節(jié)數(shù)。
使用sendto、recvfrom發(fā)送和接收數(shù)據(jù)量為0的數(shù)據(jù)報(bào)是允許的。
recvfrom返回0,并不意味著收到對(duì)端FIN(sendto,recvfrom使用的場(chǎng)景下沒有連接的概念)。
UDP 回射服務(wù)器程序:main 函數(shù)
#include "unp.h" int main(int argc, char **argv) {int sockfd;struct sockaddr_in servaddr, cliaddr;/*創(chuàng)建一個(gè)UDP套接字*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT); /*SERV_PORT 服務(wù)器的眾所周知端口*/bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));/*調(diào)用函數(shù) dg_echo 來執(zhí)行服務(wù)器的處理工作*/dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr)); }UDP 回射服務(wù)器程序:dg_echo 函數(shù)
#include "unp.h" void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen) {int n;socklen_t len;char mesg[MAXLINE];/*該函數(shù)是一個(gè)簡單的循環(huán),它使用 recvfrom 讀入下一個(gè)到達(dá)服務(wù)器端口的數(shù)據(jù)報(bào),再使用 sendto 把它發(fā)送回發(fā)送者*/for ( ; ;){len = clilen;n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);sendto(sockfd, mesg, n, 0, pcliaddr, len);} }圖中總結(jié)了TCP客戶/服務(wù)器在兩個(gè)客戶與服務(wù)器建立連接時(shí)情形。
服務(wù)器主機(jī)上有兩個(gè)已連接套接字,其中每一個(gè)都有各自的套接字接收緩沖區(qū)。
下圖展示了兩個(gè)客戶發(fā)送數(shù)據(jù)報(bào)到UDP服務(wù)器的情形。
UDP 回射客戶程序: main 函數(shù)
#include "unp.h" int main(int argc, char **argv) {int sockfd;struct sockaddr_in servaddr;if (argc != 2)err_quit("usage: udpcli <IPaddress>");/*把服務(wù)器的IP地址和端口號(hào)填入一個(gè)IPv4的套接字地址結(jié)構(gòu)*/bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, argv[1], &servaddr.sin_addr);/*創(chuàng)建一個(gè)UDP套接字,然后調(diào)用dg_cli*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));exit(0); }UDP 回射客戶程序: dg_cli 函數(shù)
#include "unp.h" void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) {int n;char sendline[MAXLINE], recvline[MAXLINE + 1];/*客戶處理循環(huán)中的四個(gè)步驟*//*使用 fgets 從標(biāo)準(zhǔn)輸入讀入一個(gè)文本行*/while (fgets(sendline, MAXLINE, fp) != NULL){/*使用 sendto 將該文本行發(fā)送給服務(wù)器*/sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);/*使用 recvfrom 讀回服務(wù)器的回射*/n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);recvline[n] = 0; /*null terminate*//*使用 fputs 把回射的文本行顯示到標(biāo)準(zhǔn)輸出*/fputs(recvline, stdout);} }數(shù)據(jù)報(bào)的丟失
如果一個(gè)客戶數(shù)據(jù)報(bào)丟失(如,被客戶主機(jī)與服務(wù)器主機(jī)之間的某個(gè)路由器丟棄),客戶將永遠(yuǎn)阻塞于 dg_cli 函數(shù)中的 recvfrom 調(diào)用,等待一個(gè)永遠(yuǎn)不會(huì)到達(dá)的服務(wù)器應(yīng)答。
防止這樣永久阻塞的一般方法是給客戶的 recvfrom 調(diào)用設(shè)置一個(gè)超時(shí)。
驗(yàn)證接收到的響應(yīng)
recvfrom 返回的IP地址(UDP數(shù)據(jù)報(bào)的源IP地址)不是我們所發(fā)送數(shù)據(jù)報(bào)的目的IP地址。
保證應(yīng)答的源地址與請(qǐng)求的目的地址相同的方法:
- 一個(gè)解決辦法是:得到由 recvfrom 返回的IP地址后,客戶通過在DNS中查找服務(wù)器主機(jī)的名字來驗(yàn)證該主機(jī)的域名(而不是它的IP地址)。
- 另一個(gè)解決辦法是:UDP服務(wù)器給服務(wù)器主機(jī)上配置的每個(gè)IP地址創(chuàng)建一個(gè)套接字,用bind捆綁每個(gè)IP地址到各自的套接字,然后在所有這些套接字上使用 select (等待其中任何一個(gè)變得可讀),再從可讀的套接字給出應(yīng)答。
學(xué)習(xí)參考資料:
《UNIX網(wǎng)絡(luò)編程 卷1:套接字聯(lián)網(wǎng)API》 第3版總結(jié)
以上是生活随笔為你收集整理的《UNIX网络编程 卷1:套接字联网API》学习笔记——基本UDP套接字编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [FROM VIJOS]安装服务器
- 下一篇: 制作dnf脚本Java_易语言制作DNF